/* JSON-P implementation for Prototype.js somewhat by Dan Dean (http://www.dandean.com)
 *
 * *HEAVILY* based on Tobie Langel's version: http://gist.github.com/145466.
 * Might as well just call this an iteration.
 *
 * This version introduces:
 * - Partial integration with Ajax.Responders (Thanks to @sr3d for the kick in this direction)
 * - Compatibility with Prototype 1.7 (Thanks to @soung3 for the bug report)
 * - Will not break if page lacks a <head> element
 *
 * See examples in README for usage
 *
 * VERSION 1.1.1
 *
 * new Ajax.JSONRequest(url, options);
 * - url (String): JSON-P endpoint url.
 * - options (Object): Configuration options for the request.
 */
Ajax.JSONRequest = Class.create(Ajax.Base, (function() {
  var id = 0, head = document.getElementsByTagName('head')[0] || document.body;
  return {
    initialize: function($super, url, options) {
      $super(options);
      this.options.url = url;
      this.options.callbackParamName = this.options.callbackParamName || 'callback';
      this.options.timeout = this.options.timeout || 10; // Default timeout: 10 seconds
      this.options.invokeImmediately = (!Object.isUndefined(this.options.invokeImmediately)) ? this.options.invokeImmediately : true ;

      if (!Object.isUndefined(this.options.parameters) && Object.isString(this.options.parameters)) {
        this.options.parameters = this.options.parameters.toQueryParams();
      }

      if (this.options.invokeImmediately) {
        this.request();
      }
    },

    /**
     *  Ajax.JSONRequest#_cleanup() -> undefined
     *  Cleans up after the request
     **/
    _cleanup: function() {
      if (this.timeout) {
        clearTimeout(this.timeout);
        this.timeout = null;
      }
      if (this.transport && Object.isElement(this.transport)) {
        this.transport.remove();
        this.transport = null;
      }
    },

    /**
     *  Ajax.JSONRequest#request() -> undefined
     *  Invokes the JSON-P request lifecycle
     **/
    request: function() {

      // Define local vars
      var response = new Ajax.JSONResponse(this);
      var key = this.options.callbackParamName,
        name = '_prototypeJSONPCallback_' + (id++),
        complete = function() {
          if (Object.isFunction(this.options.onComplete)) {
            this.options.onComplete.call(this, response);
          }
          Ajax.Responders.dispatch('onComplete', this, response);
        }.bind(this);

      // Add callback as a parameter and build request URL
      this.options.parameters[key] = name;
      var url = this.options.url + ((this.options.url.include('?') ? '&' : '?') + Object.toQueryString(this.options.parameters));

      // Define callback function
      window[name] = function(json) {
        this._cleanup(); // Garbage collection
        window[name] = undefined;

        response.status = 200;
        response.statusText = "OK";
        response.setResponseContent(json);

        if (Object.isFunction(this.options.onSuccess)) {
          this.options.onSuccess.call(this, response);
        }
        Ajax.Responders.dispatch('onSuccess', this, response);

        complete();

      }.bind(this);

      this.transport = new Element('script', { type: 'text/javascript', src: url });

      if (Object.isFunction(this.options.onCreate)) {
        this.options.onCreate.call(this, response);
      }
      Ajax.Responders.dispatch('onCreate', this);

      head.appendChild(this.transport);

      this.timeout = setTimeout(function() {
        this._cleanup();
        window[name] = Prototype.emptyFunction;
        if (Object.isFunction(this.options.onFailure)) {
          response.status = 504;
          response.statusText = "Gateway Timeout";
          this.options.onFailure.call(this, response);
        }
        complete();
      }.bind(this), this.options.timeout * 1000);
    },
    toString: function() { return "[object Ajax.JSONRequest]"; }
  };
})());

Ajax.JSONResponse = Class.create({
  initialize: function(request) {
    this.request = request;
  },
  request: undefined,
  status: 0,
  statusText: '',
  responseJSON: undefined,
  responseText: undefined,
  setResponseContent: function(json) {
    this.responseJSON = json;
    this.responseText = Object.toJSON(json);
  },
  getTransport: function() {
    if (this.request) return this.request.transport;
  },
  toString: function() { return "[object Ajax.JSONResponse]"; }
});

/* JSON-P implementation for Prototype.js somewhat by Dan Dean (http://www.dandean.com)
 *
 * *HEAVILY* based on Tobie Langel's version: http://gist.github.com/145466.
 * Might as well just call this an iteration.
 *
 * This version introduces:
 * - Support for predefined callbacks (Necessary for OAuth signed requests, by @rboyce)
 * - Partial integration with Ajax.Responders (Thanks to @sr3d for the kick in this direction)
 * - Compatibility with Prototype 1.7 (Thanks to @soung3 for the bug report)
 * - Will not break if page lacks a <head> element
 *
 * See examples in README for usage
 *
 * VERSION 1.1.2
 *
 * new Ajax.JSONRequest(url, options);
 * - url (String): JSON-P endpoint url.
 * - options (Object): Configuration options for the request.
 */
Ajax.JSONRequest = Class.create(Ajax.Base, (function() {
  var id = 0, head = document.getElementsByTagName('head')[0] || document.body;
  return {
    initialize: function($super, url, options) {
      $super(options);
      this.options.url = url;
      this.options.callbackParamName = this.options.callbackParamName || 'callback';
      this.options.timeout = this.options.timeout || 10; // Default timeout: 10 seconds
      this.options.invokeImmediately = (!Object.isUndefined(this.options.invokeImmediately)) ? this.options.invokeImmediately : true ;

      if (!Object.isUndefined(this.options.parameters) && Object.isString(this.options.parameters)) {
        this.options.parameters = this.options.parameters.toQueryParams();
      }

      if (this.options.invokeImmediately) {
        this.request();
      }
    },

    /**
     *  Ajax.JSONRequest#_cleanup() -> undefined
     *  Cleans up after the request
     **/
    _cleanup: function() {
      if (this.timeout) {
        clearTimeout(this.timeout);
        this.timeout = null;
      }
      if (this.transport && Object.isElement(this.transport)) {
        this.transport.remove();
        this.transport = null;
      }
    },

    /**
     *  Ajax.JSONRequest#request() -> undefined
     *  Invokes the JSON-P request lifecycle
     **/
    request: function() {

      // Define local vars
      var response = new Ajax.JSONResponse(this);
      var key = this.options.callbackParamName,
        name = '_prototypeJSONPCallback_' + (id++),
        complete = function() {
          if (Object.isFunction(this.options.onComplete)) {
            this.options.onComplete.call(this, response);
          }
          Ajax.Responders.dispatch('onComplete', this, response);
        }.bind(this);

      // If the callback parameter is already defined, use that
      if (this.options.parameters[key] !== undefined) {
        name = this.options.parameters[key];
      }
      // Otherwise, add callback as a parameter
      else {
        this.options.parameters[key] = name;
      }

      // Build request URL
      this.options.parameters[key] = name;
      var url = this.options.url + ((this.options.url.include('?') ? '&' : '?') + Object.toQueryString(this.options.parameters));

      // Define callback function
      window[name] = function(json) {
        this._cleanup(); // Garbage collection
        window[name] = undefined;

        response.status = 200;
        response.statusText = "OK";
        response.setResponseContent(json);

        if (Object.isFunction(this.options.onSuccess)) {
          this.options.onSuccess.call(this, response);
        }
        Ajax.Responders.dispatch('onSuccess', this, response);

        complete();

      }.bind(this);

      this.transport = new Element('script', { type: 'text/javascript', src: url });

      if (Object.isFunction(this.options.onCreate)) {
        this.options.onCreate.call(this, response);
      }
      Ajax.Responders.dispatch('onCreate', this);

      head.appendChild(this.transport);

      this.timeout = setTimeout(function() {
        this._cleanup();
        window[name] = Prototype.emptyFunction;
        if (Object.isFunction(this.options.onFailure)) {
          response.status = 504;
          response.statusText = "Gateway Timeout";
          this.options.onFailure.call(this, response);
        }
        complete();
      }.bind(this), this.options.timeout * 1000);
    },
    toString: function() { return "[object Ajax.JSONRequest]"; }
  };
})());

Ajax.JSONResponse = Class.create({
  initialize: function(request) {
    this.request = request;
  },
  request: undefined,
  status: 0,
  statusText: '',
  responseJSON: undefined,
  responseText: undefined,
  setResponseContent: function(json) {
    this.responseJSON = json;
    this.responseText = Object.toJSON(json);
  },
  getTransport: function() {
    if (this.request) return this.request.transport;
  },
  toString: function() { return "[object Ajax.JSONResponse]"; }
});


module RailsSettings
  class SettingObject < ActiveRecord::Base
    self.table_name = 'settings'

    belongs_to :target, :polymorphic => true

    validates_presence_of :var, :target_type
    validate do
      errors.add(:value, "Invalid setting value") unless value.is_a? Hash

      unless _target_class.default_settings[var.to_sym]
        errors.add(:var, "#{var} is not defined!")
      end
    end

    serialize :value, Hash

    if RailsSettings.can_protect_attributes?
      # attr_protected can not be used here because it touches the database which is not connected yet.
      # So allow no attributes and override <tt>#sanitize_for_mass_assignment</tt>
      attr_accessible
    end

    REGEX_SETTER = /\A([a-z]\w+)=\Z/i
    REGEX_GETTER = /\A([a-z]\w+)\Z/i

    def respond_to?(method_name, include_priv=false)
      super || method_name.to_s =~ REGEX_SETTER || _setting?(method_name)
    end

    def method_missing(method_name, *args, &block)
      if block_given?
        super
      else
        if attribute_names.include?(method_name.to_s.sub('=',''))
          super
        elsif method_name.to_s =~ REGEX_SETTER && args.size == 1
          _set_value($1, args.first)
        elsif method_name.to_s =~ REGEX_GETTER && args.size == 0
          _get_value($1)
        else
          super
        end
      end
    end

  protected
    if RailsSettings.can_protect_attributes?
      # Simulate attr_protected by removing all regular attributes
      def sanitize_for_mass_assignment(attributes, role = nil)
        attributes.except('id', 'var', 'value', 'target_id', 'target_type', 'created_at', 'updated_at')
      end
    end

  private
    def _get_value(name)
      if value[name].nil?
        _target_class.default_settings[var.to_sym][name]
      else
        value[name]
      end
    end

    def _set_value(name, v)
      if value[name] != v
        value_will_change!

        if v.nil?
          value.delete(name)
        else
          value[name] = v
        end
      end
    end

    def _target_class
      target_type.constantize
    end

    def _setting?(method_name)
      _target_class.default_settings[var.to_sym].keys.include?(method_name.to_s)
    end
  end
end

module RailsSettings
  class SettingObject < ActiveRecord::Base
    self.table_name = 'settings'

    belongs_to :target, :polymorphic => true

    validates_presence_of :var, :target_type
    validate do
      errors.add(:value, "Invalid setting value") unless value.is_a? Hash

      unless _target_class.default_settings[var.to_sym]
        errors.add(:var, "#{var} is not defined!")
      end
    end

    serialize :value, Hash

    if RailsSettings.can_protect_attributes?
      # attr_protected can not be used here because it touches the database which is not connected yet.
      # So allow no attributes and override <tt>#sanitize_for_mass_assignment</tt>
      attr_accessible
    end

    REGEX_SETTER = /\A([a-z]\w+)=\Z/i
    REGEX_GETTER = /\A([a-z]\w+)\Z/i

    def respond_to?(method_name, include_priv=false)
      super || method_name.to_s =~ REGEX_SETTER || _setting?(method_name)
    end

    def method_missing(method_name, *args, &block)
      if block_given?
        super
      else
        if attribute_names.include?(method_name.to_s.sub('=',''))
          super
        elsif method_name.to_s =~ REGEX_SETTER && args.size == 1
          _set_value($1, args.first)
        elsif method_name.to_s =~ REGEX_GETTER && args.size == 0
          _get_value($1)
        else
          super
        end
      end
    end

  protected
    if RailsSettings.can_protect_attributes?
      # Simulate attr_protected by removing all regular attributes
      def sanitize_for_mass_assignment(attributes, role = nil)
        attributes.except('id', 'var', 'value', 'target_id', 'target_type', 'created_at', 'updated_at')
      end
    end

  private
    def _get_value(name)
      if value[name].nil?
        if _target_class.default_settings[var.to_sym][name].respond_to?(:call)
          _target_class.default_settings[var.to_sym][name].call(target)
        else
          _target_class.default_settings[var.to_sym][name]
        end
      else
        value[name]
      end
    end

    def _set_value(name, v)
      if value[name] != v
        value_will_change!

        if v.nil?
          value.delete(name)
        else
          value[name] = v
        end
      end
    end

    def _target_class
      target_type.constantize
    end

    def _setting?(method_name)
      _target_class.default_settings[var.to_sym].keys.include?(method_name.to_s)
    end
  end
end


test('Classy exists', function() {
  ok(typeof(Class) == 'function',
    'Class is a function defined globally');
});

test('$noConflict unsets Class from window', function() {
  var original = Class;

  var Classy = Class.$noConflict();

  equals(typeof(Class), 'undefined',
    '$noConflict unsets Class from window');

  same(Classy, original,
    'the returned Class is the same as the original');

  // cleanup
  window.Class = original;
});

test('$super calls parent method', function() {
  var Greeter = Class.$extend({
    greeting: function() { return 'Armin!'; }
  });
  var Spaniard = Greeter.$extend({
    greeting: function() { return 'Hola, ' + this.$super(); }
  });
  var Englishman = Greeter.$extend({
    greeting: function() { return 'Hello, ' + this.$super(); }
  });
  same((Spaniard().greeting()), 'Hola, Armin!',
    'Spanish greeting generated.');
  same((Englishman().greeting()), 'Hello, Armin!',
    'English greeting generated.');
});

test('$extend exists and works', function() {
  same(typeof(Class.$extend), 'function',
    'basic class has $extend function');

  var SubClass = Class.$extend({});

  same(typeof(SubClass.$extend), 'function',
    'subclasses also receive $extend');

  same(SubClass.$extend, Class.$extend,
    'base class and subclass have same $extend function');
});

test('classes can be used with or without `new`.', function() {
  var pig1 = new Animal('pig');
  var pig2 = Animal('pig');

  same(pig1, pig2,
    'Animal instances are the same when made with or without `new`');
});

test('__init__ is called with correct arguments', function() {
  var Instrument = Class.$extend({
    __init__: function(volume) {
      this.volume = volume;
    }
  });
  flute = Instrument(20);
  cymbal = Instrument(100);
  equal(flute.volume, 20);
  equal(cymbal.volume, 100);
});

test('basic classical inheritence works', function() {
  garfield = HouseCat({funny: true});
  heathcliff = HouseCat({funny: false});

  equal(garfield.funny, true,
    'attribute was set on instance');
  equal(heathcliff.funny, false,
    'attribute was set on instance');
  equal(typeof(HouseCat().funny), 'undefined',
    'base HouseCat is not affected by subclass mutations');
});

test('instanceof works', function() {
  var lion = Lion();
  ok(lion instanceof Lion,
    'instanceof works on class instances');
  ok(lion instanceof Cat,
    'instanceof works on subclass instances');
  ok(lion instanceof Animal,
    'instanceof works on deep subclass instances');
  ok(!(lion instanceof HouseCat),
    'isinsinstance is false when expected');
});

test('mixins work', function() {
  FatMixin = {
    'is_fat': true,
    'describe': function() {
      return this.name + ' is fat!';
    }
  };
  FatCat = Cat.$extend({
    '__include__': [FatMixin]
  });
  garfield = FatCat({name:'Garfield'});

  ok(garfield.is_fat,
    'mixin attribute is defined');
  equal(garfield.describe(), 'Garfield is fat!',
    'mixin has access to corrent `this.');
});

test('exercise test methods', function() {
  var tick = Parasite();
  var leo = Lion();
  var garfield = HouseCat();

  ok(!(garfield.scary),
    'Cat instances are not scary.');

  ok(leo.scary,
    'Lion instances are scary.');

  equal(garfield.health, 100,
    'default health is 100');

  tick.eat(garfield);

  equal(garfield.health, 95,
    'tick removes 5 health');

  ok(!(garfield.dead()),
    'still not dead');

  leo.eat(garfield);

  ok(garfield.dead(),
    'garfield loses a life');
});

test('non-new creation calls __init__ just once', function() {
  var catched = [];
  var Test = Class.$extend({
    __init__ : function() {
      catched.push(true);
    }
  });
  Test();
  new Test();
  equal(catched.length, 2);
});

test('class attributes work', function() {
  var Test = Class.$extend({
    __classvars__ : {
      foo: 23,
      bar: 'test'
    },
    __init__ : function() {
      this.foo = 42;
    }
  });

  equal(Test.foo, 23, 'Test.foo is 23');
  equal(Test.bar, 'test', 'Test.bar is "test"');
  equal(Test().foo, 42, 'Test().foo is 42');
});

test('patching in prototypes', function() {
  var called = [];
  var Test = Class.$extend({
    __init__ : function() {
      called.push(42);
    },
    getFoo : function() {
      return this.foo;
    },
    toString : function() {
      return this.foo + ' ' + this.bar;
    }
  });
  var data = {'foo': 23, 'bar': 42};
  var obj = Test.$withData(data);
  equal(obj.foo, 23, 'Test.foo is 23');
  equal(obj.bar, 42, 'Test.bar is 42');
  equal(obj.getFoo(), obj.foo, 'getFoo() returns foo');
  // IE bug we cannot support
  if (!navigator.userAgent.match(/MSIE/))
    equal(obj.toString(), '23 42', 'Test.toString() is "23 42"');
  equal(called.length, 0, 'constructor was never called');
});

test('$class gives class access', function() {
  var Test = Class.$extend({
    __classvars__ : {'classattr': 42}
  });
  equal(Test().$class.classattr, 42, 'classattr is 42');
});

test('class variable inheritance', function() {
  var Test = Class.$extend({
    __classvars__ : {
      foo: 23,
      bar: 'test'
    }
  });
  var SubTest = Test.$extend({
    __classvars__ : {
      bar: 'subtest'
    }
  });
  var SubSubTest = SubTest.$extend({
    __classvars__ : {
      foo: 999
    }
  });

  ok(SubTest.foo, 'SubTest also has a foo');
  equal(SubTest.foo, Test.foo, 'SubTest.foo is Test.foo');
  equal(SubTest.bar, 'subtest', 'SubTest.bar has been overridden');
  equal(SubSubTest.bar, SubTest.bar, 'SubSubTest.bar is Test.bar');
  equal(SubSubTest.foo, 999, 'SubSubTest.foo has been overridden');
});

test('Classy exists', function() {
  ok(typeof(Class) == 'function',
    'Class is a function defined globally');
});

test('$noConflict unsets Class from window', function() {
  var original = Class;

  var Classy = Class.$noConflict();

  equals(typeof(Class), 'undefined',
    '$noConflict unsets Class from window');

  same(Classy, original,
    'the returned Class is the same as the original');

  // cleanup
  window.Class = original;
});

test('$super calls parent method', function() {
  var Greeter = Class.$extend({
    greeting: function() { return 'Armin!'; }
  });
  var Spaniard = Greeter.$extend({
    greeting: function() { return 'Hola, ' + this.$super(); }
  });
  var Englishman = Greeter.$extend({
    greeting: function() { return 'Hello, ' + this.$super(); }
  });
  same((Spaniard().greeting()), 'Hola, Armin!',
    'Spanish greeting generated.');
  same((Englishman().greeting()), 'Hello, Armin!',
    'English greeting generated.');
});

test('$extend exists and works', function() {
  same(typeof(Class.$extend), 'function',
    'basic class has $extend function');

  var SubClass = Class.$extend({});

  same(typeof(SubClass.$extend), 'function',
    'subclasses also receive $extend');

  same(SubClass.$extend, Class.$extend,
    'base class and subclass have same $extend function');
});

test('classes can be used with or without `new`.', function() {
  var pig1 = new Animal('pig');
  var pig2 = Animal('pig');

  same(pig1, pig2,
    'Animal instances are the same when made with or without `new`');
});

test('__init__ is called with correct arguments', function() {
  var Instrument = Class.$extend({
    __init__: function(volume) {
      this.volume = volume;
    }
  });
  flute = Instrument(20);
  cymbal = Instrument(100);
  equal(flute.volume, 20);
  equal(cymbal.volume, 100);
});

test('basic classical inheritence works', function() {
  garfield = HouseCat({funny: true});
  heathcliff = HouseCat({funny: false});

  equal(garfield.funny, true,
    'attribute was set on instance');
  equal(heathcliff.funny, false,
    'attribute was set on instance');
  equal(typeof(HouseCat().funny), 'undefined',
    'base HouseCat is not affected by subclass mutations');
});

test('inheritance of getters and setters works', function() {
  cat = Cat();
  parasite = Parasite();
  lion = Lion();

  equal(cat.furryness, 20,
    'getter was inherted from base')

  equal(parasite.furryness, 0,
    'getters can override base')

  equal(lion.furryness, 40,
    'getters can use $super to get base getter')
});

test('instanceof works', function() {
  var lion = Lion();
  ok(lion instanceof Lion,
    'instanceof works on class instances');
  ok(lion instanceof Cat,
    'instanceof works on subclass instances');
  ok(lion instanceof Animal,
    'instanceof works on deep subclass instances');
  ok(!(lion instanceof HouseCat),
    'isinsinstance is false when expected');
});

test('mixins work', function() {
  FatMixin = {
    'is_fat': true,
    'describe': function() {
      return this.name + ' is fat!';
    }
  };
  FatCat = Cat.$extend({
    '__include__': [FatMixin]
  });
  garfield = FatCat({name:'Garfield'});

  ok(garfield.is_fat,
    'mixin attribute is defined');
  equal(garfield.describe(), 'Garfield is fat!',
    'mixin has access to corrent `this.');
});

test('exercise test methods', function() {
  var tick = Parasite();
  var leo = Lion();
  var garfield = HouseCat();

  ok(!(garfield.scary),
    'Cat instances are not scary.');

  ok(leo.scary,
    'Lion instances are scary.');

  equal(garfield.health, 100,
    'default health is 100');

  tick.eat(garfield);

  equal(garfield.health, 95,
    'tick removes 5 health');

  ok(!(garfield.dead()),
    'still not dead');

  leo.eat(garfield);

  ok(garfield.dead(),
    'garfield loses a life');
});

test('non-new creation calls __init__ just once', function() {
  var catched = [];
  var Test = Class.$extend({
    __init__ : function() {
      catched.push(true);
    }
  });
  Test();
  new Test();
  equal(catched.length, 2);
});

test('class attributes work', function() {
  var Test = Class.$extend({
    __classvars__ : {
      foo: 23,
      bar: 'test'
    },
    __init__ : function() {
      this.foo = 42;
    }
  });

  equal(Test.foo, 23, 'Test.foo is 23');
  equal(Test.bar, 'test', 'Test.bar is "test"');
  equal(Test().foo, 42, 'Test().foo is 42');
});

test('patching in prototypes', function() {
  var called = [];
  var Test = Class.$extend({
    __init__ : function() {
      called.push(42);
    },
    getFoo : function() {
      return this.foo;
    },
    toString : function() {
      return this.foo + ' ' + this.bar;
    }
  });
  var data = {'foo': 23, 'bar': 42};
  var obj = Test.$withData(data);
  equal(obj.foo, 23, 'Test.foo is 23');
  equal(obj.bar, 42, 'Test.bar is 42');
  equal(obj.getFoo(), obj.foo, 'getFoo() returns foo');
  // IE bug we cannot support
  if (!navigator.userAgent.match(/MSIE/))
    equal(obj.toString(), '23 42', 'Test.toString() is "23 42"');
  equal(called.length, 0, 'constructor was never called');
});

test('$class gives class access', function() {
  var Test = Class.$extend({
    __classvars__ : {'classattr': 42}
  });
  equal(Test().$class.classattr, 42, 'classattr is 42');
});

test('class variable inheritance', function() {
  var Test = Class.$extend({
    __classvars__ : {
      foo: 23,
      bar: 'test'
    }
  });
  var SubTest = Test.$extend({
    __classvars__ : {
      bar: 'subtest'
    }
  });
  var SubSubTest = SubTest.$extend({
    __classvars__ : {
      foo: 999
    }
  });

  ok(SubTest.foo, 'SubTest also has a foo');
  equal(SubTest.foo, Test.foo, 'SubTest.foo is Test.foo');
  equal(SubTest.bar, 'subtest', 'SubTest.bar has been overridden');
  equal(SubSubTest.bar, SubTest.bar, 'SubSubTest.bar is Test.bar');
  equal(SubSubTest.foo, 999, 'SubSubTest.foo has been overridden');
});


/*
    Pynetinfo - A python module for controlling linux network interfaces
    Copyright (C) 2010  Sassan Panahinejad (sassan@sassan.me.uk)
    www.sassan.me.uk
    pypi.python.org/pypi/pynetinfo/

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
#include <Python.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/route.h>

#include "netinfo.h"

#define IF_COUNT 64

PyObject *netinfo_list_active_devs(PyObject *self, PyObject *args)
{
    int ret, fd;
    struct ifreq *ifr, *ifend;
    struct ifreq ifs[IF_COUNT];
    struct ifconf ifc;
    int i = 1;
    PyObject *tuple = PyTuple_New(0);

    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); /* open a socket to examine */
    if (fd < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        return NULL;
    }
    ifc.ifc_len = sizeof(ifs);
    ifc.ifc_req = ifs;

    ret = ioctl(fd, SIOCGIFCONF, &ifc);
    if (ret < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        return NULL;
    }
    ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
    for (ifr = ifc.ifc_req; ifr < ifend; ifr++)
    {
//         printf("dev: %s\n", ifr->ifr_name);
        _PyTuple_Resize(&tuple, i);
        PyTuple_SET_ITEM(tuple, i++-1, Py_BuildValue("s", ifr->ifr_name));
    }
    return tuple;
}

PyObject *netinfo_list_devs(PyObject *self, PyObject *args)
{
    FILE *devlist = fopen("/proc/net/dev", "r");
    char buffer[256], *c, *end;
    int i = 1;
    PyObject *tuple = PyTuple_New(0);
    while (fgets(buffer, 256, devlist)) {
        end = strchr(buffer, ':');
        if (!end)
            continue;
        *end = '\0';
        for (c = buffer; *c == ' '; c++) ;
//         printf("dev: %s\n", c);
        _PyTuple_Resize(&tuple, i);
        PyTuple_SET_ITEM(tuple, i++-1, Py_BuildValue("s", c));
    }
    return tuple;
}

PyObject *netinfo_get_addr(PyObject *self, PyObject *args, int cmd)
{
    int ret, fd;
    struct ifreq ifreq;
    char *dev;
    struct sockaddr_in *sin;
    char hwaddr[18];
    fd = socket(AF_INET, SOCK_DGRAM, 0); /* open a socket to examine */
    if (fd < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        return NULL;
    }
    ret = PyArg_ParseTuple(args, "s", &dev); /* parse argument */
    if (!ret)
        return NULL;
    memset(&ifreq, 0, sizeof(struct ifreq));
    strncpy(ifreq.ifr_name, dev, IFNAMSIZ-1);
    ifreq.ifr_addr.sa_family = AF_INET;
    ret = ioctl(fd, cmd, &ifreq, sizeof(struct ifreq));
    if (ret < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        return NULL;
    }
    switch (cmd) {
        case SIOCGIFADDR:
            sin = (struct sockaddr_in *)&(ifreq.ifr_ifru.ifru_addr);
            return Py_BuildValue("s", inet_ntoa(sin->sin_addr));
        case SIOCGIFNETMASK:
            sin = (struct sockaddr_in *)&(ifreq.ifr_ifru.ifru_netmask);
            return Py_BuildValue("s", inet_ntoa(sin->sin_addr));
        case SIOCGIFBRDADDR:
            sin = (struct sockaddr_in *)&(ifreq.ifr_ifru.ifru_broadaddr);
            return Py_BuildValue("s", inet_ntoa(sin->sin_addr));
        case SIOCGIFHWADDR:
            snprintf(hwaddr, 18, "%02X:%02X:%02X:%02X:%02X:%02X",
                        (unsigned char)ifreq.ifr_ifru.ifru_hwaddr.sa_data[0],
                        (unsigned char)ifreq.ifr_ifru.ifru_hwaddr.sa_data[1],
                        (unsigned char)ifreq.ifr_ifru.ifru_hwaddr.sa_data[2],
                        (unsigned char)ifreq.ifr_ifru.ifru_hwaddr.sa_data[3],
                        (unsigned char)ifreq.ifr_ifru.ifru_hwaddr.sa_data[4],
                        (unsigned char)ifreq.ifr_ifru.ifru_hwaddr.sa_data[5]);
            return Py_BuildValue("s", hwaddr);
    }
    return NULL;
}

PyObject *netinfo_get_ip(PyObject *self, PyObject *args)
{
    return netinfo_get_addr(self, args, SIOCGIFADDR);
}

PyObject *netinfo_get_netmask(PyObject *self, PyObject *args)
{
    return netinfo_get_addr(self, args, SIOCGIFNETMASK);
}

PyObject *netinfo_get_broadcast(PyObject *self, PyObject *args)
{
    return netinfo_get_addr(self, args, SIOCGIFBRDADDR);
}

PyObject *netinfo_get_hwaddr(PyObject *self, PyObject *args)
{
    return netinfo_get_addr(self, args, SIOCGIFHWADDR);
}

PyObject *netinfo_set_state(PyObject *self, PyObject *args)
{
    int ret, fd, state = 0;
    struct ifreq ifreq;
    char *dev;
    ret = PyArg_ParseTuple(args, "si", &dev, &state); /* parse argument */
    if (!ret)
        return NULL;
//     ret = PyArg_ParseTuple(args, "i", &state); /* parse argument */
//     if (!ret)
//         return NULL;
    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); /* open a socket to examine */
    if (fd < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        return NULL;
    }
    memset(&ifreq, 0, sizeof(struct ifreq));
    strncpy(ifreq.ifr_name, dev, IFNAMSIZ-1);
    ret = ioctl(fd, SIOCGIFFLAGS, &ifreq);
    if (ret < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        return NULL;
    }
    if (state)
        ifreq.ifr_flags |= IFF_UP;
    else
        ifreq.ifr_flags &= ~IFF_UP;
    ret = ioctl(fd, SIOCSIFFLAGS, &ifreq);
    if (ret < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        return NULL;
    }
    return Py_None;
}

PyObject *netinfo_set_addr(PyObject *self, PyObject *args, int cmd)
{
    int ret, fd;
    struct ifreq ifreq;
    char *dev, *addr;
    struct sockaddr_in *sin;
    fd = socket(AF_INET, SOCK_DGRAM, 0); /* open a socket to examine */
    if (fd < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        return NULL;
    }
    ret = PyArg_ParseTuple(args, "ss", &dev, &addr); /* parse argument */
    if (!ret)
        return NULL;
    memset(&ifreq, 0, sizeof(struct ifreq));
    strncpy(ifreq.ifr_name, dev, IFNAMSIZ-1);
    ifreq.ifr_addr.sa_family = AF_INET;
    switch (cmd) {
        case SIOCSIFADDR:
            sin = (struct sockaddr_in *)&(ifreq.ifr_ifru.ifru_addr);
            inet_aton(addr, &sin->sin_addr);
            break;
        case SIOCSIFNETMASK:
            sin = (struct sockaddr_in *)&(ifreq.ifr_ifru.ifru_netmask);
            inet_aton(addr, &sin->sin_addr);
            break;
        case SIOCSIFBRDADDR:
            sin = (struct sockaddr_in *)&(ifreq.ifr_ifru.ifru_broadaddr);
            inet_aton(addr, &sin->sin_addr);
            break;
    }
    ret = ioctl(fd, cmd, &ifreq, sizeof(struct ifreq));
    if (ret < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        return NULL;
    }
    return Py_None;
}

PyObject *netinfo_set_ip(PyObject *self, PyObject *args)
{
    return netinfo_set_addr(self, args, SIOCSIFADDR);
}

PyObject *netinfo_set_netmask(PyObject *self, PyObject *args)
{
    return netinfo_set_addr(self, args, SIOCSIFNETMASK);
}

PyObject *netinfo_set_broadcast(PyObject *self, PyObject *args)
{
    return netinfo_set_addr(self, args, SIOCSIFBRDADDR);
}

/*
    Pynetinfo - A python module for controlling linux network interfaces
    Copyright (C) 2010  Sassan Panahinejad (sassan@sassan.me.uk)
    www.sassan.me.uk
    pypi.python.org/pypi/pynetinfo/

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
#include <Python.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/route.h>

#include "netinfo.h"

#define IF_COUNT 64

PyObject *netinfo_list_active_devs(PyObject *self, PyObject *args)
{
    int ret, fd;
    struct ifreq *ifr, *ifend;
    struct ifreq ifs[IF_COUNT];
    struct ifconf ifc;
    int i = 1;
    PyObject *tuple = PyTuple_New(0);

    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); /* open a socket to examine */
    if (fd < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        return NULL;
    }
    ifc.ifc_len = sizeof(ifs);
    ifc.ifc_req = ifs;

    ret = ioctl(fd, SIOCGIFCONF, &ifc);
    if (ret < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        close(fd);
        return NULL;
    }
    ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
    for (ifr = ifc.ifc_req; ifr < ifend; ifr++)
    {
//         printf("dev: %s\n", ifr->ifr_name);
        _PyTuple_Resize(&tuple, i);
        PyTuple_SET_ITEM(tuple, i++-1, Py_BuildValue("s", ifr->ifr_name));
    }
    close(fd);
    return tuple;
}

PyObject *netinfo_list_devs(PyObject *self, PyObject *args)
{
    FILE *devlist = fopen("/proc/net/dev", "r");
    char buffer[256], *c, *end;
    int i = 1;
    PyObject *tuple = PyTuple_New(0);
    while (fgets(buffer, 256, devlist)) {
        end = strchr(buffer, ':');
        if (!end)
            continue;
        *end = '\0';
        for (c = buffer; *c == ' '; c++) ;
//         printf("dev: %s\n", c);
        _PyTuple_Resize(&tuple, i);
        PyTuple_SET_ITEM(tuple, i++-1, Py_BuildValue("s", c));
    }
    return tuple;
}

PyObject *netinfo_get_addr(PyObject *self, PyObject *args, int cmd)
{
    int ret, fd;
    struct ifreq ifreq;
    char *dev;
    struct sockaddr_in *sin;
    char hwaddr[18];
    PyObject *rval;

    fd = socket(AF_INET, SOCK_DGRAM, 0); /* open a socket to examine */
    if (fd < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        return NULL;
    }
    ret = PyArg_ParseTuple(args, "s", &dev); /* parse argument */
    if (!ret) {
        close(fd);
        return NULL;
    }
    memset(&ifreq, 0, sizeof(struct ifreq));
    strncpy(ifreq.ifr_name, dev, IFNAMSIZ-1);
    ifreq.ifr_addr.sa_family = AF_INET;
    ret = ioctl(fd, cmd, &ifreq, sizeof(struct ifreq));
    if (ret < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        close(fd);
        return NULL;
    }
    switch (cmd) {
        case SIOCGIFADDR:
            sin = (struct sockaddr_in *)&(ifreq.ifr_ifru.ifru_addr);
            rval = Py_BuildValue("s", inet_ntoa(sin->sin_addr));
            break;
        case SIOCGIFNETMASK:
            sin = (struct sockaddr_in *)&(ifreq.ifr_ifru.ifru_netmask);
            rval = Py_BuildValue("s", inet_ntoa(sin->sin_addr));
            break;
        case SIOCGIFBRDADDR:
            sin = (struct sockaddr_in *)&(ifreq.ifr_ifru.ifru_broadaddr);
            rval = Py_BuildValue("s", inet_ntoa(sin->sin_addr));
            break;
        case SIOCGIFHWADDR:
            snprintf(hwaddr, 18, "%02X:%02X:%02X:%02X:%02X:%02X",
                        (unsigned char)ifreq.ifr_ifru.ifru_hwaddr.sa_data[0],
                        (unsigned char)ifreq.ifr_ifru.ifru_hwaddr.sa_data[1],
                        (unsigned char)ifreq.ifr_ifru.ifru_hwaddr.sa_data[2],
                        (unsigned char)ifreq.ifr_ifru.ifru_hwaddr.sa_data[3],
                        (unsigned char)ifreq.ifr_ifru.ifru_hwaddr.sa_data[4],
                        (unsigned char)ifreq.ifr_ifru.ifru_hwaddr.sa_data[5]);
            rval = Py_BuildValue("s", hwaddr);
            break;
        default:
            rval = NULL;
            break;
    }
    close(fd);
    return rval;
}

PyObject *netinfo_get_ip(PyObject *self, PyObject *args)
{
    return netinfo_get_addr(self, args, SIOCGIFADDR);
}

PyObject *netinfo_get_netmask(PyObject *self, PyObject *args)
{
    return netinfo_get_addr(self, args, SIOCGIFNETMASK);
}

PyObject *netinfo_get_broadcast(PyObject *self, PyObject *args)
{
    return netinfo_get_addr(self, args, SIOCGIFBRDADDR);
}

PyObject *netinfo_get_hwaddr(PyObject *self, PyObject *args)
{
    return netinfo_get_addr(self, args, SIOCGIFHWADDR);
}

PyObject *netinfo_set_state(PyObject *self, PyObject *args)
{
    int ret, fd, state = 0;
    struct ifreq ifreq;
    char *dev;
    ret = PyArg_ParseTuple(args, "si", &dev, &state); /* parse argument */
    if (!ret)
        return NULL;
//     ret = PyArg_ParseTuple(args, "i", &state); /* parse argument */
//     if (!ret)
//         return NULL;
    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); /* open a socket to examine */
    if (fd < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        return NULL;
    }
    memset(&ifreq, 0, sizeof(struct ifreq));
    strncpy(ifreq.ifr_name, dev, IFNAMSIZ-1);
    ret = ioctl(fd, SIOCGIFFLAGS, &ifreq);
    if (ret < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        close(fd);
        return NULL;
    }
    if (state)
        ifreq.ifr_flags |= IFF_UP;
    else
        ifreq.ifr_flags &= ~IFF_UP;
    ret = ioctl(fd, SIOCSIFFLAGS, &ifreq);
    if (ret < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        close(fd);
        return NULL;
    }
    close(fd);
    return Py_None;
}

PyObject *netinfo_set_addr(PyObject *self, PyObject *args, int cmd)
{
    int ret, fd;
    struct ifreq ifreq;
    char *dev, *addr;
    struct sockaddr_in *sin;
    fd = socket(AF_INET, SOCK_DGRAM, 0); /* open a socket to examine */
    if (fd < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        return NULL;
    }
    ret = PyArg_ParseTuple(args, "ss", &dev, &addr); /* parse argument */
    if (!ret)
        return NULL;
    memset(&ifreq, 0, sizeof(struct ifreq));
    strncpy(ifreq.ifr_name, dev, IFNAMSIZ-1);
    ifreq.ifr_addr.sa_family = AF_INET;
    switch (cmd) {
        case SIOCSIFADDR:
            sin = (struct sockaddr_in *)&(ifreq.ifr_ifru.ifru_addr);
            inet_aton(addr, &sin->sin_addr);
            break;
        case SIOCSIFNETMASK:
            sin = (struct sockaddr_in *)&(ifreq.ifr_ifru.ifru_netmask);
            inet_aton(addr, &sin->sin_addr);
            break;
        case SIOCSIFBRDADDR:
            sin = (struct sockaddr_in *)&(ifreq.ifr_ifru.ifru_broadaddr);
            inet_aton(addr, &sin->sin_addr);
            break;
    }
    ret = ioctl(fd, cmd, &ifreq, sizeof(struct ifreq));
    if (ret < 0) {
        PyErr_SetFromErrno(PyExc_Exception);
        close(fd);
        return NULL;
    }
    close(fd);
    return Py_None;
}

PyObject *netinfo_set_ip(PyObject *self, PyObject *args)
{
    return netinfo_set_addr(self, args, SIOCSIFADDR);
}

PyObject *netinfo_set_netmask(PyObject *self, PyObject *args)
{
    return netinfo_set_addr(self, args, SIOCSIFNETMASK);
}

PyObject *netinfo_set_broadcast(PyObject *self, PyObject *args)
{
    return netinfo_set_addr(self, args, SIOCSIFBRDADDR);
}


from datetime import datetime, timedelta
from random import random

from django.conf import settings
from django.db import models, IntegrityError
from django.template.loader import render_to_string
from django.core.urlresolvers import reverse, NoReverseMatch
from django.contrib.sites.models import Site
from django.contrib.auth.models import User
from django.utils.hashcompat import sha_constructor
from django.utils.translation import gettext_lazy as _

from emailconfirmation.signals import email_confirmed
from emailconfirmation.utils import get_send_mail
send_mail = get_send_mail()

# this code based in-part on django-registration

class EmailAddressManager(models.Manager):

    def add_email(self, user, email):
        try:
            email_address = self.create(user=user, email=email)
            EmailConfirmation.objects.send_confirmation(email_address)
            return email_address
        except IntegrityError:
            return None

    def get_primary(self, user):
        try:
            return self.get(user=user, primary=True)
        except EmailAddress.DoesNotExist:
            return None

    def get_users_for(self, email):
        """
        returns a list of users with the given email.
        """
        # this is a list rather than a generator because we probably want to
        # do a len() on it right away
        return [address.user for address in EmailAddress.objects.filter(
            verified=True, email=email)]

class EmailAddress(models.Model):

    user = models.ForeignKey(User)
    email = models.EmailField()
    verified = models.BooleanField(default=False)
    primary = models.BooleanField(default=False)

    objects = EmailAddressManager()

    def set_as_primary(self, conditional=False):
        old_primary = EmailAddress.objects.get_primary(self.user)
        if old_primary:
            if conditional:
                return False
            old_primary.primary = False
            old_primary.save()
        self.primary = True
        self.save()
        self.user.email = self.email
        self.user.save()
        return True

    def __unicode__(self):
        return u"%s (%s)" % (self.email, self.user)

    class Meta:
        verbose_name = _("e-mail address")
        verbose_name_plural = _("e-mail addresses")
        unique_together = (
            ("user", "email"),
        )

class EmailConfirmationManager(models.Manager):

    def confirm_email(self, confirmation_key):
        try:
            confirmation = self.get(confirmation_key=confirmation_key)
        except self.model.DoesNotExist:
            return None
        if not confirmation.key_expired():
            email_address = confirmation.email_address
            email_address.verified = True
            email_address.set_as_primary(conditional=True)
            email_address.save()
            email_confirmed.send(sender=self.model, email_address=email_address)
            return email_address

    def send_confirmation(self, email_address):
        salt = sha_constructor(str(random())).hexdigest()[:5]
        confirmation_key = sha_constructor(salt + email_address.email).hexdigest()
        current_site = Site.objects.get_current()
        # check for the url with the dotted view path
        try:
            path = reverse("emailconfirmation.views.confirm_email",
                args=[confirmation_key])
        except NoReverseMatch:
            # or get path with named urlconf instead
            path = reverse(
                "emailconfirmation_confirm_email", args=[confirmation_key])
        activate_url = u"http://%s%s" % (unicode(current_site.domain), path)
        context = {
            "user": email_address.user,
            "activate_url": activate_url,
            "current_site": current_site,
            "confirmation_key": confirmation_key,
        }
        subject = render_to_string(
            "emailconfirmation/email_confirmation_subject.txt", context)
        # remove superfluous line breaks
        subject = "".join(subject.splitlines())
        message = render_to_string(
            "emailconfirmation/email_confirmation_message.txt", context)
        send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
                  [email_address.email], priority="high")
        return self.create(
            email_address=email_address,
            sent=datetime.now(),
            confirmation_key=confirmation_key)

    def delete_expired_confirmations(self):
        for confirmation in self.all():
            if confirmation.key_expired():
                confirmation.delete()

class EmailConfirmation(models.Model):

    email_address = models.ForeignKey(EmailAddress)
    sent = models.DateTimeField()
    confirmation_key = models.CharField(max_length=40)

    objects = EmailConfirmationManager()

    def key_expired(self):
        expiration_date = self.sent + timedelta(
            days=settings.EMAIL_CONFIRMATION_DAYS)
        return expiration_date <= datetime.now()
    key_expired.boolean = True

    def __unicode__(self):
        return u"confirmation for %s" % self.email_address

    class Meta:
        verbose_name = _("e-mail confirmation")
        verbose_name_plural = _("e-mail confirmations")

from datetime import datetime, timedelta
from random import random

from django.conf import settings
from django.db import models, IntegrityError
from django.template.loader import render_to_string
from django.core.urlresolvers import reverse, NoReverseMatch
from django.contrib.sites.models import Site
from django.contrib.auth.models import User
from django.utils.hashcompat import sha_constructor
from django.utils.translation import gettext_lazy as _

from emailconfirmation.signals import email_confirmed
from emailconfirmation.utils import get_send_mail
send_mail = get_send_mail()

# this code based in-part on django-registration

class EmailAddressManager(models.Manager):

    def add_email(self, user, email):
        try:
            email_address = self.create(user=user, email=email)
            EmailConfirmation.objects.send_confirmation(email_address)
            return email_address
        except IntegrityError:
            return None

    def get_primary(self, user):
        try:
            return self.get(user=user, primary=True)
        except EmailAddress.DoesNotExist:
            return None

    def get_users_for(self, email):
        """
        returns a list of users with the given email.
        """
        # this is a list rather than a generator because we probably want to
        # do a len() on it right away
        return [address.user for address in EmailAddress.objects.filter(
            verified=True, email=email)]

class EmailAddress(models.Model):

    user = models.ForeignKey(User)
    email = models.EmailField()
    verified = models.BooleanField(default=False)
    primary = models.BooleanField(default=False)

    objects = EmailAddressManager()

    def set_as_primary(self, conditional=False):
        old_primary = EmailAddress.objects.get_primary(self.user)
        if old_primary:
            if conditional:
                return False
            old_primary.primary = False
            old_primary.save()
        self.primary = True
        self.save()
        self.user.email = self.email
        self.user.save()
        return True

    def __unicode__(self):
        return u"%s (%s)" % (self.email, self.user)

    class Meta:
        verbose_name = _("e-mail address")
        verbose_name_plural = _("e-mail addresses")
        unique_together = (
            ("user", "email"),
        )

class EmailConfirmationManager(models.Manager):

    def confirm_email(self, confirmation_key):
        try:
            confirmation = self.get(confirmation_key=confirmation_key)
        except self.model.DoesNotExist:
            return None
        if not confirmation.key_expired():
            email_address = confirmation.email_address
            email_address.verified = True
            email_address.set_as_primary(conditional=True)
            email_address.save()
            email_confirmed.send(sender=self.model, email_address=email_address)
            return email_address

    def send_confirmation(self, email_address):
        salt = sha_constructor(str(random())).hexdigest()[:5]
        confirmation_key = sha_constructor(salt + email_address.email).hexdigest()
        current_site = Site.objects.get_current()
        # check for the url with the dotted view path
        try:
            path = reverse("emailconfirmation.views.confirm_email",
                args=[confirmation_key])
        except NoReverseMatch:
            # or get path with named urlconf instead
            path = reverse(
                "emailconfirmation_confirm_email", args=[confirmation_key])
        activate_url = u"http://%s%s" % (unicode(current_site.domain), path)
        context = {
            "user": email_address.user,
            "activate_url": activate_url,
            "current_site": current_site,
            "confirmation_key": confirmation_key,
        }
        subject = render_to_string(
            "emailconfirmation/email_confirmation_subject.txt", context)
        # remove superfluous line breaks
        subject = "".join(subject.splitlines())
        message = render_to_string(
            "emailconfirmation/email_confirmation_message.txt", context)
        send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
                  [email_address.email], priority="high")
        return self.create(
            email_address=email_address,
            confirmation_key=confirmation_key)

    def delete_expired_confirmations(self):
        for confirmation in self.all():
            if confirmation.key_expired():
                confirmation.delete()

class EmailConfirmation(models.Model):

    email_address = models.ForeignKey(EmailAddress)
    sent = models.DateTimeField(auto_now_add=True)
    confirmation_key = models.CharField(max_length=40)

    objects = EmailConfirmationManager()

    def key_expired(self):
        expiration_date = self.sent + timedelta(
            days=settings.EMAIL_CONFIRMATION_DAYS)
        return expiration_date <= datetime.now()
    key_expired.boolean = True

    def __unicode__(self):
        return u"confirmation for %s" % self.email_address

    class Meta:
        verbose_name = _("e-mail confirmation")
        verbose_name_plural = _("e-mail confirmations")


%# vim:set ft=mason:
<%args>
%color
</%args>
body {
  color: <% $color{bodyFG} %>;
  background: <% $color{bodyBG} %>;
  font-family: verdana, tahoma, sans-serif;
}

#contentwrapper {
  width: 75%;
  min-width: 800px;
  margin-left: auto;
  margin-right: auto;
  margin-top: 10px auto;
  margin-bottom: 10px;
  background: <% $color{blotterBG} %>;
  border: 1px solid <% $color{blotterBorder} %>;
  -moz-border-radius: 10px;
  -webkit-border-radius: 10px;
}

h1 {
  margin-top: 10px;
  margin-bottom: 10px;
  font-weight: bold;
  font-size: 1.6em;
  text-align: center;
  color: <% $color{headerFG} %>;
}

#header h1 a {
  color: <% $color{titleFG} %>;
}

h1.title {
  text-align: left;
  margin-bottom: 0;
}

h2 {
  text-align: center;
  color: <% $color{headerFG} %>;
}

.subtitle {
  text-align: right;
  border-top: 1px black solid;
}

.feed {
  float: right;
  color: <% $color{feedLinkFG} %>;
  padding-right: 1em;
  vertical-align: middle;
  font-weight: bold;
}

dt {
  margin: 0.5em 0 0.5em 0;
}

#content, #tagline {
  font-size: 0.9em;
  padding: 8px 20px;
  margin: 10px;
  -moz-border-radius: 10px;
  -webkit-border-radius: 10px;
}

#content {
  border: 1px solid <% $color{contentBorder} %>;
  background: <% $color{contentBG} %>;
}

#tagline {
  background: <% $color{taglineBG} %>;
  border: 1px solid <% $color{taglineBorder} %>;
  color: <% $color{taglineFG} %>;
}

.pod h1, .pod h2 {
  text-align: left;
  color: <% $color{headerFG} %>;
}

a {
  color: <% $color{linkFG} %>;
  text-decoration: none;
}

a:not(.article) {
  padding-left: .2em;
  margin-left: -.2em;
  padding-right: .2em;
  margin-right: -.2em;
}

#header a:hover,
.pod    a:hover
{
  color: <% $color{linkHoverFG} %>;
  background: <% $color{linkHoverBG} %>;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
}

.pod ul li {
  margin: .25em 0;
}

.pod ul li p {
  display: inline;
}

.calendar {
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 1em;
}

.calendar th {
  padding: 5px;
  border: 1px solid <% $color{calendarHeaderCellBorder} %>;
  background-color: <% $color{calendarHeaderCellBG} %>;
}

.calendar td {
  text-align: center;
  width: 9em;
  height: 5em;
  padding: 0;
}

.calendar td.day {
  font-size: xx-large;
  height: 2.5em;
  background: <% $color{calendarIgnoredDayBG} %>;
}

.calendar td.day.advent {
  background: <% $color{calendarPastDayBG} %>;
}

.calendar td.day.advent a {
  display: block;
  color: <% $color{calendarPastDayFG} %>;
  height: 2.5em;
  padding: 0;
  margin: 0;
  vertical-align: middle;
  line-height: 2.5em;
}

.calendar td.day.advent a:hover {
  background: <% $color{calendarPastDayHoverBG} %>;
  color: <% $color{calendarPastDayHoverFG} %>;
}

.calendar td.day.advent.today {
  background: <% $color{calendarTodayBG} %>;
}

.calendar td.day.advent.today a {
  color: <% $color{calendarTodayFG} %>;
}

.calendar td.day.advent.today a:hover {
  background: <% $color{calendarTodayHoverBG} %>;
  color: <% $color{calendarTodayFG} %>;
}

.calendar td.day.advent.future {
  background: <% $color{calendarFutureDayBG} %>;
  color: <% $color{calendarFutureDayFG} %>;
}

.calendar td.day.advent.missing {
  color: <% $color{calendarMissingDayFG} %>;
  background-color: <% $color{calendarMissingDayBG} %>;
}

a img {
  border: 0;
}

blockquote {
  border-left: thick <% $color{quoteBorder} %> solid;
  padding-left: 1em;
  margin-left: 1em;
  margin-right: 1em;
}

h2#See-Also {
  padding-top: 0.4em;
  border-top: 1px solid <% $color{sectionBorder} %>;
}

#author {
  border-top: 1px solid <% $color{sectionBorder} %>;
  padding-top: 0.4em;
  text-align: center;
}

#pager {
  border-top: 1px solid <% $color{sectionBorder} %>;
  list-style-type: none;
  margin-left: 0;
  padding: 0.4em;
  position: relative;
  width: 100%;
}

#pager li {
  color: <% $color{linkDisabledFG} %>;
}

#pager .previous {
  left: 0.4em;
  position: absolute;
  width: 33%;
}

#pager .next {
  position: absolute;
  right: 0.4em;
  text-align: right;
  width: 33%;
}

/* verbatim text and code listings */

pre {
  line-height: 120%;
  padding-top: 1em;
  padding-bottom: 1em;
  background-color: <% $color{codeBG} %>;
  color: <% $color{codeFG} %>;
  font-family: monospace;
  width: 100%;
}

.code-listing {
  line-height: 120%;
  background-color: <% $color{codeBG} %>;
  color: <% $color{codeFG} %>;
  font-family: monospace;
  white-space: pre;
  border-collapse: collapse;
  width: 100%;
}

.code-listing td {
  padding: 0;
  margin: 0
}

.code-listing .line-numbers {
  background-color: <% $color{codeNumbersBG} %>;
  color: <% $color{codeNumbersFG} %>;
  border-right: 2px <% $color{codeNumbersBorder} %> solid;
  width: 3.5em;
  text-align: right;
}

.code-listing .code {
  padding-left: 1em;
}

/* PPI HTML Style */

.code-listing .keyword   { color: #89f; }
.code-listing .symbol    { color: #0cc; }
.code-listing .operator  { color: #fff; }
.code-listing .structure { color: #bf0; }
.code-listing .word      { color: #dd8; }
.code-listing .comment   { color: #0f0; }
.code-listing .pod       { color: #0f0; }
.code-listing .match     { color: #ff0; }
.code-listing .readline  { color: #caa; }
.code-listing .single,
.code-listing .double    { color: #0cf; }

/* Vim Syntax Style */

.code-listing .synComment    { color: #0f0; }
.code-listing .synConstant   { color: #0ff; }
.code-listing .synIdentifier { color: #89f; }
.code-listing .synStatement  { color: #0aa; }
.code-listing .synPreProc    { color: #fff; }
.code-listing .synType       { color: #0aa; }
.code-listing .synSpecial    { color: #fff; }
.code-listing .synUnderlined { color: #0a0; }
.code-listing .synIgnore     { color: #aaa; }
.code-listing .synError      { color: #f00; }
.code-listing .synTodo       { color: #aa0; }

/* BEGIN CHRISTMAS:  Move to "extra CSS" */

.calendar td.day.advent.missing,
.calendar td.day.missing#dec-25,
.calendar td.day.missing#dec-26 {
  background: #000;
  color: #f00;
}

.calendar td#dec-25 {
  background: #ffd700;
  font-weight: bold;
}

/* END CHRISTMAS:  Move to "extra CSS" */

%# vim:set ft=mason:
<%args>
%color
</%args>
body {
  color: <% $color{bodyFG} %>;
  background: <% $color{bodyBG} %>;
  font-family: verdana, tahoma, sans-serif;
}

#contentwrapper {
  width: 75%;
  min-width: 800px;
  margin-left: auto;
  margin-right: auto;
  margin-top: 10px auto;
  margin-bottom: 10px;
  background: <% $color{blotterBG} %>;
  border: 1px solid <% $color{blotterBorder} %>;
  -moz-border-radius: 10px;
  -webkit-border-radius: 10px;
}

h1 {
  margin-top: 10px;
  margin-bottom: 10px;
  font-weight: bold;
  font-size: 1.6em;
  text-align: center;
  color: <% $color{headerFG} %>;
}

#header h1 a {
  color: <% $color{titleFG} %>;
}

h1.title {
  text-align: left;
  margin-bottom: 0;
}

h2 {
  text-align: center;
  color: <% $color{headerFG} %>;
}

.subtitle {
  text-align: right;
  border-top: 1px black solid;
}

.feed {
  float: right;
  color: <% $color{feedLinkFG} %>;
  padding-right: 1em;
  vertical-align: middle;
  font-weight: bold;
}

dt {
  margin: 0.5em 0 0.5em 0;
}

#content, #tagline, #yearline {
  font-size: 0.9em;
  padding: 8px 20px;
  margin: 10px;
  -moz-border-radius: 10px;
  -webkit-border-radius: 10px;
}

#content {
  border: 1px solid <% $color{contentBorder} %>;
  background: <% $color{contentBG} %>;
}

#tagline, #yearline {
  background: <% $color{taglineBG} %>;
  border: 1px solid <% $color{taglineBorder} %>;
  color: <% $color{taglineFG} %>;
}

.pod h1, .pod h2 {
  text-align: left;
  color: <% $color{headerFG} %>;
}

a {
  color: <% $color{linkFG} %>;
  text-decoration: none;
}

a:not(.article) {
  padding-left: .2em;
  margin-left: -.2em;
  padding-right: .2em;
  margin-right: -.2em;
}

#header a:hover,
.pod    a:hover
{
  color: <% $color{linkHoverFG} %>;
  background: <% $color{linkHoverBG} %>;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
}

.pod ul li {
  margin: .25em 0;
}

.pod ul li p {
  display: inline;
}

.calendar {
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 1em;
}

.calendar th {
  padding: 5px;
  border: 1px solid <% $color{calendarHeaderCellBorder} %>;
  background-color: <% $color{calendarHeaderCellBG} %>;
}

.calendar td {
  text-align: center;
  width: 9em;
  height: 5em;
  padding: 0;
}

.calendar td.day {
  font-size: xx-large;
  height: 2.5em;
  background: <% $color{calendarIgnoredDayBG} %>;
}

.calendar td.day.advent {
  background: <% $color{calendarPastDayBG} %>;
}

.calendar td.day.advent a {
  display: block;
  color: <% $color{calendarPastDayFG} %>;
  height: 2.5em;
  padding: 0;
  margin: 0;
  vertical-align: middle;
  line-height: 2.5em;
}

.calendar td.day.advent a:hover {
  background: <% $color{calendarPastDayHoverBG} %>;
  color: <% $color{calendarPastDayHoverFG} %>;
}

.calendar td.day.advent.today {
  background: <% $color{calendarTodayBG} %>;
}

.calendar td.day.advent.today a {
  color: <% $color{calendarTodayFG} %>;
}

.calendar td.day.advent.today a:hover {
  background: <% $color{calendarTodayHoverBG} %>;
  color: <% $color{calendarTodayFG} %>;
}

.calendar td.day.advent.future {
  background: <% $color{calendarFutureDayBG} %>;
  color: <% $color{calendarFutureDayFG} %>;
}

.calendar td.day.advent.missing {
  color: <% $color{calendarMissingDayFG} %>;
  background-color: <% $color{calendarMissingDayBG} %>;
}

a img {
  border: 0;
}

blockquote {
  border-left: thick <% $color{quoteBorder} %> solid;
  padding-left: 1em;
  margin-left: 1em;
  margin-right: 1em;
}

h2#See-Also {
  padding-top: 0.4em;
  border-top: 1px solid <% $color{sectionBorder} %>;
}

#author {
  border-top: 1px solid <% $color{sectionBorder} %>;
  padding-top: 0.4em;
  text-align: center;
}

#pager {
  border-top: 1px solid <% $color{sectionBorder} %>;
  list-style-type: none;
  margin-left: 0;
  padding: 0.4em;
  position: relative;
  width: 100%;
}

#pager li {
  color: <% $color{linkDisabledFG} %>;
}

#pager .previous {
  left: 0.4em;
  position: absolute;
  width: 33%;
}

#pager .next {
  position: absolute;
  right: 0.4em;
  text-align: right;
  width: 33%;
}

/* verbatim text and code listings */

pre {
  line-height: 120%;
  padding-top: 1em;
  padding-bottom: 1em;
  background-color: <% $color{codeBG} %>;
  color: <% $color{codeFG} %>;
  font-family: monospace;
  width: 100%;
}

.code-listing {
  line-height: 120%;
  background-color: <% $color{codeBG} %>;
  color: <% $color{codeFG} %>;
  font-family: monospace;
  white-space: pre;
  border-collapse: collapse;
  width: 100%;
}

.code-listing td {
  padding: 0;
  margin: 0
}

.code-listing .line-numbers {
  background-color: <% $color{codeNumbersBG} %>;
  color: <% $color{codeNumbersFG} %>;
  border-right: 2px <% $color{codeNumbersBorder} %> solid;
  width: 3.5em;
  text-align: right;
}

.code-listing .code {
  padding-left: 1em;
}

/* PPI HTML Style */

.code-listing .keyword   { color: #89f; }
.code-listing .symbol    { color: #0cc; }
.code-listing .operator  { color: #fff; }
.code-listing .structure { color: #bf0; }
.code-listing .word      { color: #dd8; }
.code-listing .comment   { color: #0f0; }
.code-listing .pod       { color: #0f0; }
.code-listing .match     { color: #ff0; }
.code-listing .readline  { color: #caa; }
.code-listing .single,
.code-listing .double    { color: #0cf; }

/* Vim Syntax Style */

.code-listing .synComment    { color: #0f0; }
.code-listing .synConstant   { color: #0ff; }
.code-listing .synIdentifier { color: #89f; }
.code-listing .synStatement  { color: #0aa; }
.code-listing .synPreProc    { color: #fff; }
.code-listing .synType       { color: #0aa; }
.code-listing .synSpecial    { color: #fff; }
.code-listing .synUnderlined { color: #0a0; }
.code-listing .synIgnore     { color: #aaa; }
.code-listing .synError      { color: #f00; }
.code-listing .synTodo       { color: #aa0; }

/* BEGIN CHRISTMAS:  Move to "extra CSS" */

.calendar td.day.advent.missing,
.calendar td.day.missing#dec-25,
.calendar td.day.missing#dec-26 {
  background: #000;
  color: #f00;
}

.calendar td#dec-25 {
  background: #ffd700;
  font-weight: bold;
}

/* END CHRISTMAS:  Move to "extra CSS" */


<?php
require_once dirname(__FILE__) . '/CouchDBConnector.php';

/**
 * Connector using fsockopen() function
 */
class CouchDBFsockConnector implements CouchDBConnector
{
    /**
     * End of line
     */
    const EOL = "\r\n";

    /**
     * User agent
     */
    private $ua = 'CouchDBFsockConnector';

    /**
     * @var string Host
     */
    private $host = 'localhost';

    /**
     * @var int Port
     */
    private $port = 5984;

    /**
     * @var string Username
     */
    private $user = NULL;

    /**
     * @var string Password
     */
    private $pass = NULL;

    /**
     * @var string Base path
     */
    private $path = '/';

    /**
     * Create new instance
     * @pram string
     * @return CouchDBFsockConnector
     */
    public static function open($url)
    {
        return new self($url);
    }

    /**
     * Initialize state
     * @param string
     */
    private function __constuct($url)
    {
        foreach (parse_url($url) as $k => $v) $this->$k = $v;
        $this->path = rtrim($this->path, '/') . '/';
    }

    /**
     * Create socket
     * @param int
     * @param string
     * @return resource
     */
    protected function createSocket(&$errno, &$errstr)
    {
        return @fsockopen($this->host, $this->port, $errno, $errstr);
    }

    /**
     * Create request
     * @param string
     * @param string
     * @param string
     * @param array
     * @return string
     */
    protected function createRequest(&$request, $method, $path, $query = array())
    {
        $path = ltrim($path, '/');
        if (preg_match('~^.+/((_desing/.+/|_temp)_view|_all_docs)~', $path)) {
            foreach (array('key', 'startkey', 'endkey', 'limit', 'descending',
                'skip', 'group', 'group_level', 'reduce', 'include_docs') as $k)
            {
                if (!isset($query[$k])) continue;
                $query[$k] = json_encode($query[$k]);
            }
        }

        $request = $method .
            ' ' .
            $this->path . $path .
            (empty($query) ? '' : '?' . http_build_query($query, NULL, '&')) .
            ' ' .
            'HTTP/1.1' . self::EOL;
    }

    /**
     * Add essential headers
     * @param string
     */
    protected function addEssentialHeaders(&$request)
    {
        $request .= 'Host: ' . $this->host . self::EOL;
        $request .= 'User-Agent: ' . $this->ua . self::EOL;
        $request .= 'Accept: */*' . self::EOL;
        $request .= 'Connection: close' . self::EOL;
    }

    /**
     * Add additional headers
     * @param string
     * @param array
     */
    protected function addAdditionalHeaders(&$request, array $headers = array())
    {
        foreach ($headers as $header => $value)
            $request .= $header . ': ' . $value . self::EOL;
    }

    /**
     * Authorization headers
     * @param string
     */
    protected function addAuthorizationHeaders(&$request)
    {
        if ($this->user !== NULL)
            $request .= 'Authorization: Basic' .
                base64_encode($this->user . ':' . $this->pass) .
                self::EOL;
    }

    /**
     * Add content
     * @param string
     * @param string
     * @param bool
     */
    protected function addContent(&$request, $body = NULL, $raw = FALSE)
    {
        if ($body !== NULL) {
            $json = FALSE;
            if (!$raw) {
                $json = TRUE;
                $body = json_encode($body);
            }
            $request .= 'Content-Length: ' . strlen($body) . self::EOL;
            if ($json) $request .= 'Content-Type: application/json' . self::EOL;
        }

        $request .= self::EOL;
        $request .= $body;
    }

    /**
     * Send request
     * @param resource
     * @param string
     */
    protected function sendRequest($socket, $request)
    {
        fwrite($socket, $request);
    }

    /**
     * Get response
     * @param resource
     */
    protected function getResponse($socket)
    {
        // get data
        $response = '';
        while (!feof($socket)) $response .= fread($socket, 4096);

        // get headers
        list($headers, $body) = explode(self::EOL . self::EOL, $response, 2);
        $real_headers = array();
        $return = NULL;
        foreach (explode(self::EOL, $headers) as $header) {
            if ($return === NULL) {
                $return = $header;
                continue;
            }
            list($k, $v) = explode(':', $header, 2);
            $real_headers[strtolower($k)] = trim($v);
        }

        // chunked?
        if (isset($real_headers['transfer-encoding']) &&
            $real_headers['transfer-encoding'] === 'chunked')
        {
            $real_body = '';
            $tail = $body;
            do {
                list($head, $tail) = explode(self::EOL, $tail, 2);
                $size = hexdec($head);
                if ($size > 0) {
                    $real_body .= substr($tail, 0, $size);
                    $tail = substr($tail, $size + strlen(self::EOL));
                }
            } while ($size);

        } else $real_body = $body;

        return array($real_headers, $return, $real_body);
    }

    /**
     * Send request to database
     * @param string
     * @param string
     * @param array
     * @param mixed
     * @param bool whether body should be sent as raw
     * @param array additional headers
     * @return array
     */
    public function request($method, $path, array $query = array(), $body = NULL,
        $raw_body = FALSE, $headers = array())
    {
        if (!($s = $this->createSocket($errno, $errstr))) return (object) array(
            'error' => 'fsockopen',
            'errstr' => $errstr,
            'errno' => $errno
        );

        $this->createRequest($request, $method, $path, $query);
        $this->addEssentialHeaders($request);
        $this->addAdditionalHeaders($request, $headers);
        $this->addAuthorizationHeaders($request);
        $this->addContent($request, $body, $raw_body);
        $this->sendRequest($s, $request);
        list($headers, $return, $body) = $this->getResponse($s);
        fclose($s);

        if ($raw_body) return (object) array(
            'data' => $body,
            'content_type' => isset($headers['content-type']) ? $header['content-type'] : NULL
        );
        return json_decode($body);
    }
}

<?php
require_once dirname(__FILE__) . '/CouchDBConnector.php';

/**
 * Connector using fsockopen() function
 */
class CouchDBFsockConnector implements CouchDBConnector
{
    /**
     * End of line
     */
    const EOL = "\r\n";

    /**
     * User agent
     */
    private $ua = 'CouchDBFsockConnector';

    /**
     * @var string Host
     */
    private $host = 'localhost';

    /**
     * @var int Port
     */
    private $port = 5984;

    /**
     * @var string Username
     */
    private $user = NULL;

    /**
     * @var string Password
     */
    private $pass = NULL;

    /**
     * @var string Base path
     */
    private $path = '/';

    /**
     * Create new instance
     * @pram string
     * @return CouchDBFsockConnector
     */
    public static function open($url)
    {
        return new self($url);
    }

    /**
     * Initialize state
     * @param string
     */
    private function __constuct($url)
    {
        foreach (parse_url($url) as $k => $v) $this->$k = $v;
        $this->path = rtrim($this->path, '/') . '/';
    }

    /**
     * Create socket
     * @param int
     * @param string
     * @return resource
     */
    protected function createSocket(&$errno, &$errstr)
    {
        return @fsockopen($this->host, $this->port, $errno, $errstr);
    }

    /**
     * Create request
     * @param string
     * @param string
     * @param string
     * @param array
     * @return string
     */
    protected function createRequest(&$request, $method, $path, $query = array())
    {
        $path = ltrim($path, '/');
        if (preg_match('~^.+/((_desing/.+/|_temp)_view|_all_docs)~', $path)) {
            foreach (array('key', 'startkey', 'endkey', 'limit', 'descending',
                'skip', 'group', 'group_level', 'reduce', 'include_docs') as $k)
            {
                if (!isset($query[$k])) continue;
                $query[$k] = json_encode($query[$k]);
            }
        }

        $request = $method .
            ' ' .
            $this->path . $path .
            (empty($query) ? '' : '?' . http_build_query($query, NULL, '&')) .
            ' ' .
            'HTTP/1.1' . self::EOL;
    }

    /**
     * Add essential headers
     * @param string
     */
    protected function addEssentialHeaders(&$request)
    {
        $request .= 'Host: ' . $this->host . self::EOL;
        $request .= 'User-Agent: ' . $this->ua . self::EOL;
        $request .= 'Accept: */*' . self::EOL;
        $request .= 'Connection: close' . self::EOL;
    }

    /**
     * Add additional headers
     * @param string
     * @param array
     */
    protected function addAdditionalHeaders(&$request, array $headers = array())
    {
        foreach ($headers as $header => $value)
            $request .= $header . ': ' . $value . self::EOL;
    }

    /**
     * Authorization headers
     * @param string
     */
    protected function addAuthorizationHeaders(&$request)
    {
        if ($this->user !== NULL)
            $request .= 'Authorization: Basic ' .
                base64_encode($this->user . ':' . $this->pass) .
                self::EOL;
    }

    /**
     * Add content
     * @param string
     * @param string
     * @param bool
     */
    protected function addContent(&$request, $body = NULL, $raw = FALSE)
    {
        if ($body !== NULL) {
            $json = FALSE;
            if (!$raw) {
                $json = TRUE;
                $body = json_encode($body);
            }
            $request .= 'Content-Length: ' . strlen($body) . self::EOL;
            if ($json) $request .= 'Content-Type: application/json' . self::EOL;
        }

        $request .= self::EOL;
        $request .= $body;
    }

    /**
     * Send request
     * @param resource
     * @param string
     */
    protected function sendRequest($socket, $request)
    {
        fwrite($socket, $request);
    }

    /**
     * Get response
     * @param resource
     */
    protected function getResponse($socket)
    {
        // get data
        $response = '';
        while (!feof($socket)) $response .= fread($socket, 4096);

        // get headers
        list($headers, $body) = explode(self::EOL . self::EOL, $response, 2);
        $real_headers = array();
        $return = NULL;
        foreach (explode(self::EOL, $headers) as $header) {
            if ($return === NULL) {
                $return = $header;
                continue;
            }
            list($k, $v) = explode(':', $header, 2);
            $real_headers[strtolower($k)] = trim($v);
        }

        // chunked?
        if (isset($real_headers['transfer-encoding']) &&
            $real_headers['transfer-encoding'] === 'chunked')
        {
            $real_body = '';
            $tail = $body;
            do {
                list($head, $tail) = explode(self::EOL, $tail, 2);
                $size = hexdec($head);
                if ($size > 0) {
                    $real_body .= substr($tail, 0, $size);
                    $tail = substr($tail, $size + strlen(self::EOL));
                }
            } while ($size);

        } else $real_body = $body;

        return array($real_headers, $return, $real_body);
    }

    /**
     * Send request to database
     * @param string
     * @param string
     * @param array
     * @param mixed
     * @param bool whether body should be sent as raw
     * @param array additional headers
     * @return array
     */
    public function request($method, $path, array $query = array(), $body = NULL,
        $raw_body = FALSE, $headers = array())
    {
        if (!($s = $this->createSocket($errno, $errstr))) return (object) array(
            'error' => 'fsockopen',
            'errstr' => $errstr,
            'errno' => $errno
        );

        $this->createRequest($request, $method, $path, $query);
        $this->addEssentialHeaders($request);
        $this->addAdditionalHeaders($request, $headers);
        $this->addAuthorizationHeaders($request);
        $this->addContent($request, $body, $raw_body);
        $this->sendRequest($s, $request);
        list($headers, $return, $body) = $this->getResponse($s);
        fclose($s);

        if ($raw_body) return (object) array(
            'data' => $body,
            'content_type' => isset($headers['content-type']) ? $header['content-type'] : NULL
        );
        return json_decode($body);
    }
}


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">


# Based on gdb.rb by Jamis Buck, thanks Jamis!

module Hijack
  class GDB
    def initialize(pid)
      @pid = pid
      @verbose = Hijack.options[:debug]
      @exec_path = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['RUBY_INSTALL_NAME'] + RbConfig::CONFIG['EXEEXT'])
      attach_outside_gc
    end

    def eval(cmd)
      call("(void)rb_eval_string(#{cmd.strip.gsub(/"/, '\"').inspect})")
    end

    def quit
      return unless @gdb
      detach
      exec('quit')
      @backtrace = nil
      @gdb.close
      @gdb = nil
    end

  protected

    def previous_frame_inner_to_this_frame?
      backtrace.last =~ /previous frame inner to this frame/i
    end

    def attach_outside_gc
      @gdb = IO.popen("gdb -q #{@exec_path} #{@pid} 2>&1", 'r+')
      wait
      ensure_attached_to_ruby_process
      attached = false

      3.times do |i|
        attach unless i == 0

        if previous_frame_inner_to_this_frame? || during_gc?
          detach
          sleep 0.1
        else
          attached = true
          break
        end
      end

      unless attached
        puts
        puts "=> Tried 3 times to attach to #{@pid} whilst GC wasn't running but failed."
        puts "=> This means either the process calls GC.start frequently or GC runs are slow - try hijacking again."
        exit 1
      end

      break_on_safe_stack_unwind
    end

    def break_on_safe_stack_unwind
      safe = false
      backtrace.each do |line|
        # vm_call_method == 1.9, rb_call == 1.8
        if line =~ /(vm_call_method|rb_call)/
          frame = line.match(/^\#([\d]+)/)[1]
          safe = true
          exec("frame #{frame}")
          exec("break")
          exec("continue")
          exec("delete 1")
          break
        end
      end

      if !safe
        puts "=> WARNING: Did not detect a safe frame on which to set a breakpoint, hijack may fail."
      end
    end

    def during_gc?
      !!(call("(int)rb_during_gc()").first =~ /\$[\d]+ = 1/)
    end

    def detach
      exec("detach")
    end

    def attach
      exec("attach #{@pid}")
    end

    def ensure_attached_to_ruby_process
      unless backtrace.any? {|line| line =~ /(rb|ruby)_/}
        puts "\n=> #{@pid} doesn't appear to be a Ruby process!"
        detach
        exit 1
      end
    end

    def backtrace
      exec('bt')
    end

    def continue
      exec('continue')
    end

    def call(cmd)
      exec("call #{cmd}")
    end

    def exec(str)
      puts str if @verbose
      @gdb.puts(str)
      wait
    end

    def wait
      lines = []
      line = ''
      while result = IO.select([@gdb])
        next if result.empty?
        c = @gdb.read(1)
        break if c.nil?
        STDOUT.write(c) if @verbose
        line << c
        break if line == "(gdb) " || line == " >"
        if line[-1] == ?\n
          lines << line
          line = ""
        end
      end
      lines
    end
  end
end

# Based on gdb.rb by Jamis Buck, thanks Jamis!

module Hijack
  class GDB
    def initialize(pid)
      @pid = pid
      @verbose = Hijack.options[:debug]
      @exec_path = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['RUBY_INSTALL_NAME'] + RbConfig::CONFIG['EXEEXT'])
      attach_outside_gc
    end

    def eval(cmd)
      evaled_cmd = cmd.strip.gsub(/"/, '\"').inspect
      call("(void)rb_eval_string(#{evaled_cmd})")
    end

    def quit
      return unless @gdb
      detach
      exec('quit')
      @backtrace = nil
      @gdb.close
      @gdb = nil
    end

    protected

    def previous_frame_inner_to_this_frame?
      backtrace.last =~ /previous frame inner to this frame/i
    end

    def gdb_path
      # Check for gdb
      if File.exists?(`which gdb`.strip)
        `which gdb`.strip
      elsif File.exists?(`which ggdb`.strip)
        `which ggdb`.strip
      else
        raise "Cannot find suitable gdb!"
      end
    end

    def attach_outside_gc
      @gdb = IO.popen("#{gdb_path} -q #{@exec_path} #{@pid} 2>&1", 'r+')
      wait
      ensure_attached_to_ruby_process
      attached = false

      3.times do |i|
        attach unless i == 0

        if previous_frame_inner_to_this_frame? || during_gc?
          detach
          sleep 0.1
        else
          attached = true
          break
        end
      end

      unless attached
        puts
        puts "=> Tried 3 times to attach to #{@pid} whilst GC wasn't running but failed."
        puts "=> This means either the process calls GC.start frequently or GC runs are slow - try hijacking again."
        exit 1
      end

      break_on_safe_stack_unwind
    end

    def break_on_safe_stack_unwind
      safe = false
      backtrace.each do |line|
        # vm_call_method == 1.9, rb_call == 1.8
        if line =~ /(vm_call_method|rb_call)/
          frame = line.match(/^\#([\d]+)/)[1]
          safe = true
          exec("frame #{frame}")
          exec("break")
          exec("continue")
          exec("delete 1")
          break
        end
      end

      if !safe
        puts "=> WARNING: Did not detect a safe frame on which to set a breakpoint, hijack may fail."
      end
    end

    def during_gc?
      !!(call("(int)rb_during_gc()").first =~ /\$[\d]+ = 1/)
    end

    def detach
      exec("detach")
    end

    def attach
      exec("attach #{@pid}")
    end

    def ensure_attached_to_ruby_process
      unless backtrace.any? {|line| line =~ /(rb|ruby)_/}
        puts "\n=> #{@pid} doesn't appear to be a Ruby process!"
        detach
        exit 1
      end
    end

    def backtrace
      exec('bt')
    end

    def continue
      exec('continue')
    end

    def call(cmd)
      exec("call #{cmd}")
    end

    def exec(str)
      puts str if @verbose
      @gdb.puts(str)
      wait
    end

    def wait
      lines = []
      line = ''
      while result = IO.select([@gdb])
        next if result.empty?
        c = @gdb.read(1)
        break if c.nil?
        STDOUT.write(c) if @verbose
        line << c
        break if line == "(gdb) " || line == " >"
        if line[-1] == ?\n
          lines << line
          line = ""
        end
      end
      lines
    end
  end
end