"""Models of Zinnia"""
from datetime import datetime

from django.db import models
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.db.models.signals import post_save
from django.template.defaultfilters import striptags
from django.template.defaultfilters import linebreaks
from django.contrib.comments.moderation import moderator
from django.utils.translation import ugettext_lazy as _

from tagging.fields import TagField

from zinnia.settings import USE_BITLY
from zinnia.settings import UPLOAD_TO
from zinnia.managers import entries_published
from zinnia.managers import EntryPublishedManager
from zinnia.managers import DRAFT, HIDDEN, PUBLISHED
from zinnia.moderator import EntryCommentModerator
from zinnia.signals import ping_directories_handler
from zinnia.signals import ping_external_urls_handler

class Category(models.Model):
    """Category object for Entry"""

    title = models.CharField(_('title'), max_length=255)
    slug = models.SlugField(help_text=_('used for publication'),
                            unique=True, max_length=255)
    description = models.TextField(_('description'), blank=True)

    def entries_published_set(self):
        """Return only the entries published"""
        return entries_published(self.entry_set)

    def __unicode__(self):
        return self.title

    @models.permalink
    def get_absolute_url(self):
        return ('zinnia_category_detail', (self.slug, ))

    class Meta:
        verbose_name = _('category')
        verbose_name_plural = _('categories')
        ordering = ['title']

class Entry(models.Model):
    """Base design for publishing entry"""
    STATUS_CHOICES = ((DRAFT, _('draft')),
                      (HIDDEN, _('hidden')),
                      (PUBLISHED, _('published')))

    title = models.CharField(_('title'), max_length=255)

    image = models.ImageField(_('image'), upload_to=UPLOAD_TO,
                              blank=True, help_text=_('used for illustration'))
    content = models.TextField(_('content'))
    excerpt = models.TextField(_('excerpt'), blank=True,
                                help_text=_('optional element'))

    tags = TagField(_('tags'))
    categories = models.ManyToManyField(Category, verbose_name=_('categories'))
    related = models.ManyToManyField('self', verbose_name=_('related entries'),
                                     blank=True, null=True)

    slug = models.SlugField(help_text=_('used for publication'),
                            unique_for_date='creation_date',
                            max_length=255)

    authors = models.ManyToManyField(User, verbose_name=_('authors'),
                                     blank=True, null=False)
    status = models.IntegerField(choices=STATUS_CHOICES, default=DRAFT)
    comment_enabled = models.BooleanField(_('comment enabled'), default=True)
    pingback_enabled = models.BooleanField(_('linkback enabled'), default=True)

    creation_date = models.DateTimeField(_('creation date'), default=datetime.now)
    last_update = models.DateTimeField(_('last update'), default=datetime.now)
    start_publication = models.DateTimeField(_('start publication'),
                                             help_text=_('date start publish'),
                                             default=datetime.now)
    end_publication = models.DateTimeField(_('end publication'),
                                           help_text=_('date end publish'),
                                           default=datetime(2042, 3, 15))

    sites = models.ManyToManyField(Site, verbose_name=_('sites publication'))

    objects = models.Manager()
    published = EntryPublishedManager()

    @property
    def html_content(self):
        """Return the content correctly formatted"""
        if not '</p>' in self.content:
            return linebreaks(self.content)
        return self.content

    @property
    def previous_entry(self):
        """Return the previous entry"""
        entries = Entry.published.filter(
            creation_date__lt=self.creation_date)[:1]
        if entries:
            return entries[0]

    @property
    def next_entry(self):
        """Return the next entry"""
        entries = Entry.published.filter(
            creation_date__gt=self.creation_date).order_by('creation_date')[:1]
        if entries:
            return entries[0]

    @property
    def word_count(self):
        """Count the words of an entry"""
        return len(striptags(self.html_content).split())

    @property
    def is_actual(self):
        """Check if an entry is within publication period"""
        now = datetime.now()
        return now >= self.start_publication and now < self.end_publication

    @property
    def is_visible(self):
        """Check if an entry is visible on site"""
        return self.is_actual and self.status == PUBLISHED

    @property
    def related_published_set(self):
        """Return only related entries published"""
        return entries_published(self.related)

    @property
    def discussions(self):
        """Return published discussions"""
        from django.contrib.comments.models import Comment
        return Comment.objects.for_model(self).filter(is_public=True)

    @property
    def comments(self):
        """Return published comments"""
        return self.discussions.filter(flags=None)

    @property
    def pingbacks(self):
        """Return published pingbacks"""
        return self.discussions.filter(flags__flag='pingback')

    @property
    def trackbacks(self):
        """Return published trackbacks"""
        return self.discussions.filter(flags__flag='trackback')

    @property
    def short_url(self):
        """Return the entry's short url"""
        if not USE_BITLY:
            return False

        from django_bitly.models import Bittle

        bittle = Bittle.objects.bitlify(self)
        url = bittle and bittle.shortUrl or self.get_absolute_url()
        return url

    def __unicode__(self):
        return '%s: %s' % (self.title, self.get_status_display())

    @models.permalink
    def get_absolute_url(self):
        return ('zinnia_entry_detail', (), {
            'year': self.creation_date.strftime('%Y'),
            'month': self.creation_date.strftime('%m'),
            'day': self.creation_date.strftime('%d'),
            'slug': self.slug})

    class Meta:
        ordering = ['-creation_date']
        verbose_name = _('entry')
        verbose_name_plural = _('entries')
        permissions = (('can_view_all', 'Can view all'),
                       ('can_change_author', 'Can change author'), )

post_save.connect(ping_directories_handler, sender=Entry)
post_save.connect(ping_external_urls_handler, sender=Entry)
moderator.register(Entry, EntryCommentModerator)

"""Models of Zinnia"""
from datetime import datetime

from django.db import models
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.db.models import Q
from django.db.models.signals import post_save
from django.template.defaultfilters import striptags
from django.template.defaultfilters import linebreaks
from django.contrib.comments.moderation import moderator
from django.utils.translation import ugettext_lazy as _

from tagging.fields import TagField

from zinnia.settings import USE_BITLY
from zinnia.settings import UPLOAD_TO
from zinnia.managers import entries_published
from zinnia.managers import EntryPublishedManager
from zinnia.managers import DRAFT, HIDDEN, PUBLISHED
from zinnia.moderator import EntryCommentModerator
from zinnia.signals import ping_directories_handler
from zinnia.signals import ping_external_urls_handler

class Category(models.Model):
    """Category object for Entry"""

    title = models.CharField(_('title'), max_length=255)
    slug = models.SlugField(help_text=_('used for publication'),
                            unique=True, max_length=255)
    description = models.TextField(_('description'), blank=True)

    def entries_published_set(self):
        """Return only the entries published"""
        return entries_published(self.entry_set)

    def __unicode__(self):
        return self.title

    @models.permalink
    def get_absolute_url(self):
        return ('zinnia_category_detail', (self.slug, ))

    class Meta:
        verbose_name = _('category')
        verbose_name_plural = _('categories')
        ordering = ['title']

class Entry(models.Model):
    """Base design for publishing entry"""
    STATUS_CHOICES = ((DRAFT, _('draft')),
                      (HIDDEN, _('hidden')),
                      (PUBLISHED, _('published')))

    title = models.CharField(_('title'), max_length=255)

    image = models.ImageField(_('image'), upload_to=UPLOAD_TO,
                              blank=True, help_text=_('used for illustration'))
    content = models.TextField(_('content'))
    excerpt = models.TextField(_('excerpt'), blank=True,
                                help_text=_('optional element'))

    tags = TagField(_('tags'))
    categories = models.ManyToManyField(Category, verbose_name=_('categories'))
    related = models.ManyToManyField('self', verbose_name=_('related entries'),
                                     blank=True, null=True)

    slug = models.SlugField(help_text=_('used for publication'),
                            unique_for_date='creation_date',
                            max_length=255)

    authors = models.ManyToManyField(User, verbose_name=_('authors'),
                                     blank=True, null=False)
    status = models.IntegerField(choices=STATUS_CHOICES, default=DRAFT)
    comment_enabled = models.BooleanField(_('comment enabled'), default=True)
    pingback_enabled = models.BooleanField(_('linkback enabled'), default=True)

    creation_date = models.DateTimeField(_('creation date'), default=datetime.now)
    last_update = models.DateTimeField(_('last update'), default=datetime.now)
    start_publication = models.DateTimeField(_('start publication'),
                                             help_text=_('date start publish'),
                                             default=datetime.now)
    end_publication = models.DateTimeField(_('end publication'),
                                           help_text=_('date end publish'),
                                           default=datetime(2042, 3, 15))

    sites = models.ManyToManyField(Site, verbose_name=_('sites publication'))

    objects = models.Manager()
    published = EntryPublishedManager()

    @property
    def html_content(self):
        """Return the content correctly formatted"""
        if not '</p>' in self.content:
            return linebreaks(self.content)
        return self.content

    @property
    def previous_entry(self):
        """Return the previous entry"""
        entries = Entry.published.filter(
            creation_date__lt=self.creation_date)[:1]
        if entries:
            return entries[0]

    @property
    def next_entry(self):
        """Return the next entry"""
        entries = Entry.published.filter(
            creation_date__gt=self.creation_date).order_by('creation_date')[:1]
        if entries:
            return entries[0]

    @property
    def word_count(self):
        """Count the words of an entry"""
        return len(striptags(self.html_content).split())

    @property
    def is_actual(self):
        """Check if an entry is within publication period"""
        now = datetime.now()
        return now >= self.start_publication and now < self.end_publication

    @property
    def is_visible(self):
        """Check if an entry is visible on site"""
        return self.is_actual and self.status == PUBLISHED

    @property
    def related_published_set(self):
        """Return only related entries published"""
        return entries_published(self.related)

    @property
    def discussions(self):
        """Return published discussions"""
        from django.contrib.comments.models import Comment
        return Comment.objects.for_model(self).filter(is_public=True)

    @property
    def comments(self):
        """Return published comments"""
        # moderator approval is used by django.contrib.comments admin view,
        # when approving a comment
        return self.discussions.filter(Q(flags=None) | Q(flags__flag='moderator approval')

    @property
    def pingbacks(self):
        """Return published pingbacks"""
        return self.discussions.filter(flags__flag='pingback')

    @property
    def trackbacks(self):
        """Return published trackbacks"""
        return self.discussions.filter(flags__flag='trackback')

    @property
    def short_url(self):
        """Return the entry's short url"""
        if not USE_BITLY:
            return False

        from django_bitly.models import Bittle

        bittle = Bittle.objects.bitlify(self)
        url = bittle and bittle.shortUrl or self.get_absolute_url()
        return url

    def __unicode__(self):
        return '%s: %s' % (self.title, self.get_status_display())

    @models.permalink
    def get_absolute_url(self):
        return ('zinnia_entry_detail', (), {
            'year': self.creation_date.strftime('%Y'),
            'month': self.creation_date.strftime('%m'),
            'day': self.creation_date.strftime('%d'),
            'slug': self.slug})

    class Meta:
        ordering = ['-creation_date']
        verbose_name = _('entry')
        verbose_name_plural = _('entries')
        permissions = (('can_view_all', 'Can view all'),
                       ('can_change_author', 'Can change author'), )

post_save.connect(ping_directories_handler, sender=Entry)
post_save.connect(ping_external_urls_handler, sender=Entry)
moderator.register(Entry, EntryCommentModerator)


require "ev/ftools"
require "rbconfig"
require "rubyscript2exe"

exit	if __FILE__ == $0

module RUBYSCRIPT2EXE
end

module REQUIRE2LIB
  JUSTRUBYLIB	= ARGV.include?("--require2lib-justrubylib")
  JUSTSITELIB	= ARGV.include?("--require2lib-justsitelib")
  RUBYGEMS	= (not JUSTRUBYLIB)
  VERBOSE	= ARGV.include?("--require2lib-verbose")
  QUIET		= (ARGV.include?("--require2lib-quiet") and not VERBOSE)
  LOADED	= []

  ARGV.delete_if{|arg| arg =~ /^--require2lib-/}

  ORGDIR	= Dir.pwd
  THISFILE	= File.expand_path(__FILE__)
  LIBDIR	= File.expand_path((ENV["REQUIRE2LIB_LIBDIR"] or "."))
  LOADSCRIPT	= File.expand_path((ENV["REQUIRE2LIB_LOADSCRIPT"] or "."))
  RUBYLIBDIR	= Config::CONFIG["rubylibdir"]
  SITELIBDIR	= Config::CONFIG["sitelibdir"]

  at_exit do
    Dir.chdir(ORGDIR)

    REQUIRE2LIB.gatherlibs
  end

  def self.gatherlibs
    $stderr.puts "Gathering files..."	unless QUIET

    File.makedirs(LIBDIR)

    if RUBYGEMS
      begin
        Gem.dir
        rubygems	= true
      rescue NameError
        rubygems	= false
      end
    else
      rubygems		= false
    end

    pureruby	= true

    if rubygems
      require "fileutils"	# Hack ???

      requireablefiles	= []

      Dir.mkdir(File.expand_path("rubyscript2exe.gems", LIBDIR))
      Dir.mkdir(File.expand_path("rubyscript2exe.gems/gems", LIBDIR))
      Dir.mkdir(File.expand_path("rubyscript2exe.gems/specifications", LIBDIR))

      Gem.loaded_specs.each do |key, gem|
        $stderr.puts "Found gem #{gem.name} (#{gem.version})."	if VERBOSE

        fromdir	= File.join(gem.installation_path, "specifications")
        todir		= File.expand_path("rubyscript2exe.gems/specifications", LIBDIR)

        fromfile	= File.join(fromdir, "#{gem.full_name}.gemspec")
        tofile	= File.join(todir, "#{gem.full_name}.gemspec")

        File.copy(fromfile, tofile)

        fromdir	= gem.full_gem_path
        todir		= File.expand_path(File.join("rubyscript2exe.gems/gems", gem.full_name), LIBDIR)

        Dir.copy(fromdir, todir)

        Dir.find(todir).each do |file|
          if File.file?(file)
            gem.require_paths.each do |lib|
              unless lib.empty?
                lib	= File.expand_path(lib, todir)
                lib	= lib + "/"

                requireablefiles << file[lib.length..-1]	if file =~ /^#{lib.gsub('+', '\+')}/
              end
            end
          end
        end
      end
    end

    ($" + LOADED).each do |req|
      catch :found do
        $:.each do |lib|
          fromfile	= File.expand_path(req, lib)
          tofile	= File.expand_path(req, LIBDIR)

          if File.file?(fromfile)
            unless fromfile == tofile or fromfile == THISFILE
              unless (rubygems and requireablefiles.include?(req))	# ??? requireablefiles might be a little dangerous.
                if (not JUSTRUBYLIB and not JUSTSITELIB) or
                   (JUSTRUBYLIB and fromfile.include?(RUBYLIBDIR)) or
                   (JUSTSITELIB and fromfile.include?(SITELIBDIR))
                  $stderr.puts "Found #{fromfile} ."		if VERBOSE

                  File.makedirs(File.dirname(tofile))	unless File.directory?(File.dirname(tofile))
                  File.copy(fromfile, tofile)

                  pureruby	= false	unless req =~ /\.(rbw?|ruby)$/i
                else
                  $stderr.puts "Skipped #{fromfile} ."	if VERBOSE
                end
              end
            end

            throw :found
          end
        end

        #$stderr.puts "Can't find #{req} ."	unless req =~ /^ev\// or QUIET
        #$stderr.puts "Can't find #{req} ."	unless req =~ /^(\w:)?[\/\\]/ or QUIET
      end
    end

    $stderr.puts "Not all required files are pure Ruby."	unless pureruby	if VERBOSE

    unless LOADSCRIPT == ORGDIR
      File.open(LOADSCRIPT, "w") do |f|
        f.puts "module RUBYSCRIPT2EXE"
        f.puts "  REQUIRE2LIB_FROM_APP={}"

        RUBYSCRIPT2EXE.class_variables.each do |const|
          const	= const[2..-1]

          f.puts "  REQUIRE2LIB_FROM_APP[:#{const}]=#{RUBYSCRIPT2EXE.send(const).inspect}"
        end

        f.puts "  REQUIRE2LIB_FROM_APP[:rubygems]=#{rubygems.inspect}"
        f.puts "end"
      end
    end
  end
end

module Kernel
  alias :require2lib_load :load
  def load(filename, wrap=false)
    REQUIRE2LIB::LOADED << filename	unless REQUIRE2LIB::LOADED.include?(filename)

    require2lib_load(filename, wrap)
  end
end

require "ev/ftools"
require "rbconfig"
require "rubyscript2exe"

exit	if __FILE__ == $0

module RUBYSCRIPT2EXE
end

module REQUIRE2LIB
  JUSTRUBYLIB	= ARGV.include?("--require2lib-justrubylib")
  JUSTSITELIB	= ARGV.include?("--require2lib-justsitelib")
  RUBYGEMS	= (not JUSTRUBYLIB)
  VERBOSE	= ARGV.include?("--require2lib-verbose")
  QUIET		= (ARGV.include?("--require2lib-quiet") and not VERBOSE)
  LOADED	= []

  ARGV.delete_if{|arg| arg =~ /^--require2lib-/}

  ORGDIR	= Dir.pwd
  THISFILE	= File.expand_path(__FILE__)
  LIBDIR	= File.expand_path((ENV["REQUIRE2LIB_LIBDIR"] or "."))
  LOADSCRIPT	= File.expand_path((ENV["REQUIRE2LIB_LOADSCRIPT"] or "."))
  RUBYLIBDIR	= RbConfig::CONFIG["rubylibdir"]
  SITELIBDIR	= RbConfig::CONFIG["sitelibdir"]

  at_exit do
    Dir.chdir(ORGDIR)

    REQUIRE2LIB.gatherlibs
  end

  def self.gatherlibs
    $stderr.puts "Gathering files..."	unless QUIET

    FileUtils.makedirs(LIBDIR)

    if RUBYGEMS
      begin
        Gem.dir
        rubygems	= true
      rescue NameError
        rubygems	= false
      end
    else
      rubygems		= false
    end

    pureruby	= true

    if rubygems
      require "fileutils"	# Hack ???

      requireablefiles	= []

      Dir.mkdir(File.expand_path("rubyscript2exe.gems", LIBDIR))
      Dir.mkdir(File.expand_path("rubyscript2exe.gems/gems", LIBDIR))
      Dir.mkdir(File.expand_path("rubyscript2exe.gems/specifications", LIBDIR))

      Gem.loaded_specs.each do |key, gem|
        $stderr.puts "Found gem #{gem.name} (#{gem.version})."	if VERBOSE

        fromdir	= File.join(gem.installation_path, "specifications")
        todir		= File.expand_path("rubyscript2exe.gems/specifications", LIBDIR)

        fromfile	= File.join(fromdir, "#{gem.full_name}.gemspec")
        tofile	= File.join(todir, "#{gem.full_name}.gemspec")

        FileUtils.copy(fromfile, tofile)

        fromdir	= gem.full_gem_path
        todir		= File.expand_path(File.join("rubyscript2exe.gems/gems", gem.full_name), LIBDIR)

        Dir.copy(fromdir, todir)

        Dir.find(todir).each do |file|
          if File.file?(file)
            gem.require_paths.each do |lib|
              unless lib.empty?
                lib	= File.expand_path(lib, todir)
                lib	= lib + "/"

                requireablefiles << file[lib.length..-1]	if file =~ /^#{lib.gsub('+', '\+')}/
              end
            end
          end
        end
      end
    end

    ($" + LOADED).each do |req|
      catch :found do
        $:.each do |lib|
          fromfile	= File.expand_path(req, lib)
          tofile	= File.expand_path(req, LIBDIR)

          if File.file?(fromfile)
            unless fromfile == tofile or fromfile == THISFILE
              unless (rubygems and requireablefiles.include?(req))	# ??? requireablefiles might be a little dangerous.
                if (not JUSTRUBYLIB and not JUSTSITELIB) or
                   (JUSTRUBYLIB and fromfile.include?(RUBYLIBDIR)) or
                   (JUSTSITELIB and fromfile.include?(SITELIBDIR))
                  $stderr.puts "Found #{fromfile} ."		if VERBOSE

                  FileUtils.makedirs(File.dirname(tofile))	unless File.directory?(File.dirname(tofile))
                  FileUtils.copy(fromfile, tofile)

                  pureruby	= false	unless req =~ /\.(rbw?|ruby)$/i
                else
                  $stderr.puts "Skipped #{fromfile} ."	if VERBOSE
                end
              end
            end

            throw :found
          end
        end

        #$stderr.puts "Can't find #{req} ."	unless req =~ /^ev\// or QUIET
        #$stderr.puts "Can't find #{req} ."	unless req =~ /^(\w:)?[\/\\]/ or QUIET
      end
    end

    $stderr.puts "Not all required files are pure Ruby."	unless pureruby	if VERBOSE

    unless LOADSCRIPT == ORGDIR
      File.open(LOADSCRIPT, "w") do |f|
        f.puts "module RUBYSCRIPT2EXE"
        f.puts "  REQUIRE2LIB_FROM_APP={}"

        RUBYSCRIPT2EXE.class_variables.each do |const|
          const	= const[2..-1]

          f.puts "  REQUIRE2LIB_FROM_APP[:#{const}]=#{RUBYSCRIPT2EXE.send(const).inspect}"
        end

        f.puts "  REQUIRE2LIB_FROM_APP[:rubygems]=#{rubygems.inspect}"
        f.puts "end"
      end
    end
  end
end

module Kernel
  alias :require2lib_load :load
  def load(filename, wrap=false)
    REQUIRE2LIB::LOADED << filename	unless REQUIRE2LIB::LOADED.include?(filename)

    require2lib_load(filename, wrap)
  end
end


/**
  Onion HTTP server library
  Copyright (C) 2010-2018 David Moreno Montero and others

  This library is free software; you can redistribute it and/or
  modify it under the terms of, at your choice:

  a. the Apache License Version 2.0.

  b. the GNU General Public License as published by the
  Free Software Foundation; either version 2.0 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 both licenses, if not see
  <http://www.gnu.org/licenses/> and
  <http://www.apache.org/licenses/LICENSE-2.0>.
*/

#include <ev.h>
#include <stdlib.h>
#include <semaphore.h>

#include "poller.h"
#include "log.h"
#include "low.h"

struct onion_poller_t {
  struct ev_loop *loop;
  sem_t sem;
  volatile int stop;
};

struct onion_poller_slot_t {
  int fd;
  int timeout;
  int type;
  ev_io ev;
  void *data;
  int (*f) (void *);
  void *shutdown_data;
  void (*shutdown) (void *);
  onion_poller *poller;
};

/// Create a new slot for the poller
onion_poller_slot *onion_poller_slot_new(int fd, int (*f) (void *), void *data) {
  onion_poller_slot *ret = onion_low_calloc(1, sizeof(onion_poller_slot));
  ret->fd = fd;
  ret->f = f;
  ret->data = data;
  ret->type = EV_READ | EV_WRITE;

  return ret;
}

/// Cleans a poller slot. Do not call if already on the poller (onion_poller_add). Use onion_poller_remove instead.
void onion_poller_slot_free(onion_poller_slot * el) {
  if (el->poller)
    ev_io_stop(el->poller->loop, &el->ev);
  if (el->shutdown)
    el->shutdown(el->shutdown_data);
}

/// Sets the shutdown function for this poller slot
void onion_poller_slot_set_shutdown(onion_poller_slot * el,
                                    void (*shutdown) (void *), void *data) {
  el->shutdown = shutdown;
  el->shutdown_data = data;
}

/// Sets the timeout for this slot. Current implementation takes ms, but then it rounds to seconds.
void onion_poller_slot_set_timeout(onion_poller_slot * el, int timeout_ms) {
  el->timeout = timeout_ms;
}

/// Sets the polling type: read/write/other. O_POLL_READ | O_POLL_WRITE | O_POLL_OTHER
void onion_poller_slot_set_type(onion_poller_slot * el, int type) {
  el->type = 0;
  if (type & O_POLL_READ)
    el->type |= EV_READ;
  if (type & O_POLL_WRITE)
    el->type |= EV_WRITE;
}

/// Create a new poller
onion_poller *onion_poller_new(int aprox_n) {
  onion_poller *ret = onion_low_calloc(1, sizeof(onion_poller));
  ret->loop = ev_default_loop(0);
  sem_init(&ret->sem, 0, 1);
  return ret;
}

/// Frees the poller. It first stops it.
void onion_poller_free(onion_poller * p) {
  onion_low_free(p);
}

static void event_callback(struct ev_loop *loop, ev_io * w, int revents) {
  onion_poller_slot *s = w->data;
  int res = s->f(s->data);
  if (res < 0) {
    onion_poller_slot_free(s);
  }
}

/// Adds a slot to the poller
int onion_poller_add(onion_poller * poller, onion_poller_slot * el) {
  el->poller = poller;

  // normally would use ev_io_init bellow, but gcc on F18+ give a
  // "dereferencing type-punned pointer will break strict-aliasing rules" error
  // So the macro must be expanded and this is what we get. In the future this may
  // give bugs on libevent if this changes.

  //ev_io_init(&el->ev, event_callback, el->fd, el->type);
  // expands to ev_init + ev_io_set. ev_init expand more or less as bellow

  //ev_init(&el->ev, event_callback);
  {
    ev_watcher *ew = (void *)(&el->ev); // ew must exit to prevent the before mentioned error.
    ew->active = 0;
    ew->pending = 0;
    ew->priority = 0;
    el->ev.cb = event_callback;
  }

  ev_io_set(&el->ev, el->fd, el->type);

  el->ev.data = el;
//      if (el->timeout>0){
//              event_add(el->ev, &tv);
//      }
//      else{
//              event_add(el->ev, NULL);
//      }
  ev_io_start(poller->loop, &el->ev);
  return 1;
}

/// Removes a fd from the poller
int onion_poller_remove(onion_poller * poller, int fd) {
  ONION_ERROR("FIXME!! not removing fd %d", fd);
  return -1;
}

/// Gets the poller to do some modifications as change shutdown
onion_poller_slot *onion_poller_get(onion_poller * poller, int fd) {
  ONION_ERROR("Not implemented! Use epoll poller.");
  return NULL;
}

/// Do the polling. If on several threads, this is done in every thread.
void onion_poller_poll(onion_poller * poller) {
  ev_default_fork();
  ev_loop_fork(poller->loop);

  poller->stop = 0;
  while (!poller->stop) {
    sem_wait(&poller->sem);
    ev_run(poller->loop, EVLOOP_ONESHOT);
    sem_post(&poller->sem);
  }
}

/// Stops the polling. This only marks the flag, and should be cancelled with pthread_cancel.
void onion_poller_stop(onion_poller * poller) {
  poller->stop = 1;
  ev_break(poller->loop, EVBREAK_ALL);
}

// Not implemented for libev
void onion_poller_set_queue_size_per_thread(onion_poller * poller, size_t count) {
  ONION_WARNING
      ("onion_poller_queue_size_per_thread only used with epoll polling, not libev.");
}

/**
  Onion HTTP server library
  Copyright (C) 2010-2018 David Moreno Montero and others

  This library is free software; you can redistribute it and/or
  modify it under the terms of, at your choice:

  a. the Apache License Version 2.0.

  b. the GNU General Public License as published by the
  Free Software Foundation; either version 2.0 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 both licenses, if not see
  <http://www.gnu.org/licenses/> and
  <http://www.apache.org/licenses/LICENSE-2.0>.
*/

#include <ev.h>
#include <stdlib.h>
#include <semaphore.h>

#include "poller.h"
#include "log.h"
#include "low.h"

struct onion_poller_t {
  struct ev_loop *loop;
  sem_t sem;
  volatile int stop;
};

struct onion_poller_slot_t {
  int fd;
  int timeout;
  int type;
  ev_io ev;
  void *data;
  int (*f) (void *);
  void *shutdown_data;
  void (*shutdown) (void *);
  onion_poller *poller;
};

/// Create a new slot for the poller
onion_poller_slot *onion_poller_slot_new(int fd, int (*f) (void *), void *data) {
  onion_poller_slot *ret = onion_low_calloc(1, sizeof(onion_poller_slot));
  ret->fd = fd;
  ret->f = f;
  ret->data = data;
  ret->type = EV_READ | EV_WRITE;

  return ret;
}

/// Cleans a poller slot. Do not call if already on the poller (onion_poller_add). Use onion_poller_remove instead.
void onion_poller_slot_free(onion_poller_slot * el) {
  if (el->poller)
    ev_io_stop(el->poller->loop, &el->ev);
  if (el->shutdown)
    el->shutdown(el->shutdown_data);
}

/// Sets the shutdown function for this poller slot
void onion_poller_slot_set_shutdown(onion_poller_slot * el,
                                    void (*shutdown) (void *), void *data) {
  el->shutdown = shutdown;
  el->shutdown_data = data;
}

/// Sets the timeout for this slot. Current implementation takes ms, but then it rounds to seconds.
void onion_poller_slot_set_timeout(onion_poller_slot * el, int timeout_ms) {
  el->timeout = timeout_ms;
}

/// Sets the polling type: read/write/other. O_POLL_READ | O_POLL_WRITE | O_POLL_OTHER
void onion_poller_slot_set_type(onion_poller_slot * el, onion_poller_slot_type_e type) {
  el->type = 0;
  if (type & O_POLL_READ)
    el->type |= EV_READ;
  if (type & O_POLL_WRITE)
    el->type |= EV_WRITE;
}

/// Create a new poller
onion_poller *onion_poller_new(int aprox_n) {
  onion_poller *ret = onion_low_calloc(1, sizeof(onion_poller));
  ret->loop = ev_default_loop(0);
  sem_init(&ret->sem, 0, 1);
  return ret;
}

/// Frees the poller. It first stops it.
void onion_poller_free(onion_poller * p) {
  onion_low_free(p);
}

static void event_callback(struct ev_loop *loop, ev_io * w, int revents) {
  onion_poller_slot *s = w->data;
  int res = s->f(s->data);
  if (res < 0) {
    onion_poller_slot_free(s);
  }
}

/// Adds a slot to the poller
int onion_poller_add(onion_poller * poller, onion_poller_slot * el) {
  el->poller = poller;

  // normally would use ev_io_init bellow, but gcc on F18+ give a
  // "dereferencing type-punned pointer will break strict-aliasing rules" error
  // So the macro must be expanded and this is what we get. In the future this may
  // give bugs on libevent if this changes.

  //ev_io_init(&el->ev, event_callback, el->fd, el->type);
  // expands to ev_init + ev_io_set. ev_init expand more or less as bellow

  //ev_init(&el->ev, event_callback);
  {
    ev_watcher *ew = (void *)(&el->ev); // ew must exit to prevent the before mentioned error.
    ew->active = 0;
    ew->pending = 0;
    ew->priority = 0;
    el->ev.cb = event_callback;
  }

  ev_io_set(&el->ev, el->fd, el->type);

  el->ev.data = el;
//      if (el->timeout>0){
//              event_add(el->ev, &tv);
//      }
//      else{
//              event_add(el->ev, NULL);
//      }
  ev_io_start(poller->loop, &el->ev);
  return 1;
}

/// Removes a fd from the poller
int onion_poller_remove(onion_poller * poller, int fd) {
  ONION_ERROR("FIXME!! not removing fd %d", fd);
  return -1;
}

/// Gets the poller to do some modifications as change shutdown
onion_poller_slot *onion_poller_get(onion_poller * poller, int fd) {
  ONION_ERROR("Not implemented! Use epoll poller.");
  return NULL;
}

/// Do the polling. If on several threads, this is done in every thread.
void onion_poller_poll(onion_poller * poller) {
  ev_default_fork();
  ev_loop_fork(poller->loop);

  poller->stop = 0;
  while (!poller->stop) {
    sem_wait(&poller->sem);
    ev_run(poller->loop, EVLOOP_ONESHOT);
    sem_post(&poller->sem);
  }
}

/// Stops the polling. This only marks the flag, and should be cancelled with pthread_cancel.
void onion_poller_stop(onion_poller * poller) {
  poller->stop = 1;
  ev_break(poller->loop, EVBREAK_ALL);
}

// Not implemented for libev
void onion_poller_set_queue_size_per_thread(onion_poller * poller, size_t count) {
  ONION_WARNING
      ("onion_poller_queue_size_per_thread only used with epoll polling, not libev.");
}


/* -*- mode: javascript; c-basic-offset: 4; indent-tabs-mode: nil -*- */

//
// Dalliance Genome Explorer
// (c) Thomas Down 2006-2013
//
// jbjson.js -- query JBrowse-style REST data stores
//

if (typeof(require) !== 'undefined') {
    var das = require('./das');
    var DASStylesheet = das.DASStylesheet;
    var DASStyle = das.DASStyle;
    var DASFeature = das.DASFeature;
    var DASGroup = das.DASGroup;

    var utils = require('./utils');
    var shallowCopy = utils.shallowCopy;

    var spans = require('./spans');
    var Range = spans.Range;
    var union = spans.union;
    var intersection = spans.intersection;
}

function JBrowseStore(base, query) {
    this.base = base;
    this.query = query;
}

function jbori(strand) {
    if (strand > 0)
        return '+';
    else if (strand < 0)
        return '-';
}

JBrowseStore.prototype.features = function(segment, opts, callback) {
    opts = opts || {};

    url = this.base + '/features/' + segment.name;

    var filters = [];
    if (this.query) {
	   filters.push(this.query);
    }
    if (segment.isBounded) {
    	filters.push('start=' + segment.start);
    	filters.push('end=' + segment.end);
    }
    if (filters.length > 0) {
	    url = url + '?' + filters.join('&');
    }

    var req = new XMLHttpRequest();
    req.onreadystatechange = function() {
	if (req.readyState == 4) {
	    if (req.status >= 300) {
		    callback(null, 'Error code ' + req.status);
	    } else {
		var jf = JSON.parse(req.response)['features'];
		var features = [];
		for (fi = 0; fi < jf.length; ++fi) {
		    var j = jf[fi];

		    var f = new DASFeature();
		    f.segment = segment.name;
		    f.min = (j['start'] | 0) + 1;
		    f.max = j['end'] | 0;
		    if (j.name) {
			f.label = j.name;
		    }
                    if (j.strand)
                        f.orientation = jbori(j.strand);
		    f.type = j.type || 'unknown';

                    if (j.subfeatures && j.subfeatures.length > 0) {
                        f.id = j.uniqueID;

                        var blocks = [];
                        var cds = [];
                        var all = [];

                        for (var si = 0; si < j.subfeatures.length; ++si) {
                            var sj = j.subfeatures[si];
                            var sf = shallowCopy(f);
                            sf.min = sj.start + 1;
                            sf.max = sj.end;
                            sf.groups = [f];

                            all.push(sf);
                            blocks.push(new Range(sf.min, sf.max));
                            if (sj.type === 'CDS')
                                cds.push(sf);
                        }

                        if (cds.length > 0) {
                            spans = union(blocks);
                            var txGroup = shallowCopy(f);
                            txGroup.type = 'transcript';
                            spans.ranges().forEach(function(exon) {
                                features.push({
                                    segment:     segment.name,
                                    min:         exon.min(),
                                    max:         exon.max(),
                                    orientation: f.orientation,
                                    groups:      [txGroup],
                                    type:        'transcript'
                                });
                            });

                            var tlGroup = shallowCopy(f);
                            cds.forEach(function(cdsExon) {
                                cdsExon.type = 'translation'
                                cdsExon.groups = [tlGroup];
                                features.push(cdsExon);
                            });
                        } else {
                            all.forEach(function(f) {
                                features.push(f);
                            });
                        }
                    } else {
		        features.push(f);
                    }
		}
		callback(features);
	    }
	}

    };

    req.open('GET', url, true);
    req.responseType = 'text';
    req.send();
}

if (typeof(module) !== 'undefined') {
    module.exports = {
        JBrowseStore: JBrowseStore
    };
}

/* -*- mode: javascript; c-basic-offset: 4; indent-tabs-mode: nil -*- */

//
// Dalliance Genome Explorer
// (c) Thomas Down 2006-2013
//
// jbjson.js -- query JBrowse-style REST data stores
//

if (typeof(require) !== 'undefined') {
    var das = require('./das');
    var DASStylesheet = das.DASStylesheet;
    var DASStyle = das.DASStyle;
    var DASFeature = das.DASFeature;
    var DASGroup = das.DASGroup;

    var utils = require('./utils');
    var shallowCopy = utils.shallowCopy;

    var spans = require('./spans');
    var Range = spans.Range;
    var union = spans.union;
    var intersection = spans.intersection;
}

function JBrowseStore(base, query) {
    this.base = base;
    this.query = query;
}

function jbori(strand) {
    if (strand > 0)
        return '+';
    else if (strand < 0)
        return '-';
}

JBrowseStore.prototype.features = function(segment, opts, callback) {
    opts = opts || {};

    var url = this.base + '/features/' + segment.name;

    var filters = [];
    if (this.query) {
	   filters.push(this.query);
    }
    if (segment.isBounded) {
    	filters.push('start=' + segment.start);
    	filters.push('end=' + segment.end);
    }
    if (filters.length > 0) {
	    url = url + '?' + filters.join('&');
    }

    var req = new XMLHttpRequest();
    req.onreadystatechange = function() {
	if (req.readyState == 4) {
	    if (req.status >= 300) {
		    callback(null, 'Error code ' + req.status);
	    } else {
		var jf = JSON.parse(req.response)['features'];
		var features = [];
		for (var fi = 0; fi < jf.length; ++fi) {
		    var j = jf[fi];

		    var f = new DASFeature();
		    f.segment = segment.name;
		    f.min = (j['start'] | 0) + 1;
		    f.max = j['end'] | 0;
		    if (j.name) {
			f.label = j.name;
		    }
                    if (j.strand)
                        f.orientation = jbori(j.strand);
		    f.type = j.type || 'unknown';

                    if (j.subfeatures && j.subfeatures.length > 0) {
                        f.id = j.uniqueID;

                        var blocks = [];
                        var cds = [];
                        var all = [];

                        for (var si = 0; si < j.subfeatures.length; ++si) {
                            var sj = j.subfeatures[si];
                            var sf = shallowCopy(f);
                            sf.min = sj.start + 1;
                            sf.max = sj.end;
                            sf.groups = [f];

                            all.push(sf);
                            blocks.push(new Range(sf.min, sf.max));
                            if (sj.type === 'CDS')
                                cds.push(sf);
                        }

                        if (cds.length > 0) {
                            spans = union(blocks);
                            var txGroup = shallowCopy(f);
                            txGroup.type = 'transcript';
                            spans.ranges().forEach(function(exon) {
                                features.push({
                                    segment:     segment.name,
                                    min:         exon.min(),
                                    max:         exon.max(),
                                    orientation: f.orientation,
                                    groups:      [txGroup],
                                    type:        'transcript'
                                });
                            });

                            var tlGroup = shallowCopy(f);
                            cds.forEach(function(cdsExon) {
                                cdsExon.type = 'translation'
                                cdsExon.groups = [tlGroup];
                                features.push(cdsExon);
                            });
                        } else {
                            all.forEach(function(f) {
                                features.push(f);
                            });
                        }
                    } else {
		        features.push(f);
                    }
		}
		callback(features);
	    }
	}

    };

    req.open('GET', url, true);
    req.responseType = 'text';
    req.send();
}

if (typeof(module) !== 'undefined') {
    module.exports = {
        JBrowseStore: JBrowseStore
    };
}


<?php
require_once 'simpletest/unit_tester.php';
if (realpath($_SERVER['PHP_SELF']) == __FILE__) {
  error_reporting(E_ALL | E_STRICT);
  require_once 'simpletest/autorun.php';
}

require_once 'lib/bucket.inc.php';

class NoDependencies {}
class ExtendsNoDependencies extends NoDependencies {}

class SingleClassDependency {
  public $val;
  function __construct(NoDependencies $val) {
    $this->val = $val;
  }
}

class DefaultValue {
  public $val;
  function __construct($val = 42) {
    $this->val = $val;
  }
}

class UnTypedDependency {
  public $val;
  function __construct($val) {
    $this->val = $val;
  }
}

interface AnInterface {};

class ConcreteImplementation implements AnInterface {}

class DependsOnInterface {
  public $val;
  function __construct(AnInterface $val) {
    $this->val = $val;
  }
}

class TestFactory {
  public $invoked = false;
  function new_NoDependencies($container) {
    $this->invoked = true;
    return new NoDependencies();
  }
  function new_ConcreteImplementation($container) {
    $this->invoked = true;
    return new NoDependencies();
  }
}

class RequireUndefinedClass {
  function __construct(ClassThatDoesntExist $autoloaded) {}
}

class TriedToAutoloadException extends Exception {
  public $classname;
  function __construct($classname) {
    $this->classname = $classname;
    parent::__construct();
  }
}

function test_autoload_fail($classname) {
  throw new TriedToAutoloadException($classname);
}

class TestUnderscoreCallFactory {
  public $invoked = false;
  function __call($name, $args) {
    $this->invoked = true;
    return new StdClass();
  }
}

class TestOfBucketAutoload extends UnitTestCase {
  function setUp() {
    $this->spl_autoload_functions = spl_autoload_functions();
    if ($this->spl_autoload_functions) {
      foreach ($this->spl_autoload_functions as $fn) {
        spl_autoload_unregister($fn);
      }
    }
  }
  function tearDown() {
    if (spl_autoload_functions()) {
      foreach (spl_autoload_functions() as $fn) {
        spl_autoload_unregister($fn);
      }
    }
    if ($this->spl_autoload_functions) {
      foreach ($this->spl_autoload_functions as $fn) {
        spl_autoload_register($fn);
      }
    }
  }
  function test_undefined_class_triggers_autoload() {
    spl_autoload_register('test_autoload_fail');
    $bucket = new bucket_Container();
    $this->expectException('TriedToAutoloadException');
    $bucket->create('RequireUndefinedClass');
  }
  function test_autoload_gets_canonical_classname() {
    spl_autoload_register('test_autoload_fail');
    $bucket = new bucket_Container();
    try {
      $bucket->create('RequireUndefinedClass');
      $this->fail("Expected TriedToAutoloadException");
    } catch (TriedToAutoloadException $ex) {
      $this->assertEqual($ex->classname, 'ClassThatDoesntExist');
    }
  }
}

class TestOfBucketResolving extends UnitTestCase {
  function test_can_create_empty_container() {
    $bucket = new bucket_Container();
  }
  function test_can_create_class_with_no_dependencies() {
    $bucket = new bucket_Container();
    $this->assertIsA($bucket->create('NoDependencies'), 'NoDependencies');
  }
  function test_can_create_class_with_class_dependency() {
    $bucket = new bucket_Container();
    $o = $bucket->create('SingleClassDependency');
    $this->assertIsA($o, 'SingleClassDependency');
    $this->assertIsA($o->val, 'NoDependencies');
  }
  function test_can_create_class_with_default_value() {
    $bucket = new bucket_Container();
    $o = $bucket->create('DefaultValue');
    $this->assertIsA($o, 'DefaultValue');
    $this->assertEqual($o->val, 42);
  }
  function test_barks_on_untyped_dependency() {
    $bucket = new bucket_Container();
    try {
      $bucket->create('UnTypedDependency');
      $this->fail("Expected exception");
    } catch (bucket_CreationException $ex) {
      $this->pass("Exception caught");
    }
  }
  function test_barks_on_interface_dependency_when_unregistered() {
    $bucket = new bucket_Container();
    try {
      $bucket->create('DependsOnInterface');
      $this->fail("Expected exception");
    } catch (bucket_CreationException $ex) {
      $this->pass("Exception caught");
    }
  }
  function test_can_create_class_with_interface_dependency() {
    $bucket = new bucket_Container();
    $bucket->registerImplementation('AnInterface', 'ConcreteImplementation');
    $o = $bucket->create('DependsOnInterface');
    $this->assertIsA($o, 'DependsOnInterface');
    $this->assertIsA($o->val, 'ConcreteImplementation');
  }
  function test_can_set_different_implementation_for_concrete_class() {
    $bucket = new bucket_Container();
    $bucket->registerImplementation('NoDependencies', 'ExtendsNoDependencies');
    $o = $bucket->create('SingleClassDependency');
    $this->assertIsA($o, 'SingleClassDependency');
    $this->assertIsA($o->val, 'ExtendsNoDependencies');
  }
}

class TestOfBucketContainer extends UnitTestCase {
  function test_get_creates_new_object() {
    $bucket = new bucket_Container();
    $this->assertIsA($bucket->get('NoDependencies'), 'NoDependencies');
  }
  function test_get_returns_same_instance_on_subsequent_calls() {
    $bucket = new bucket_Container();
    $this->assertSame(
      $bucket->get('NoDependencies'),
      $bucket->get('NoDependencies'));
  }
}

class TestOfBucketFactory extends UnitTestCase {
  function test_container_delegates_to_factory_method() {
    $factory = new TestFactory();
    $bucket = new bucket_Container($factory);
    $this->assertIsA($bucket->get('NoDependencies'), 'NoDependencies');
    $this->assertTrue($factory->invoked);
  }
  function test_container_can_return_different_implementation() {
    $bucket = new bucket_Container(new TestFactory());
    $this->assertIsA($bucket->get('ConcreteImplementation'), 'NoDependencies');
  }
  function test_container_delegates_to_factory_callback() {
    $factory = new TestFactory();
    $factory->new_defaultvalue = create_function('', 'return new StdClass();');
    // For PHP 5.3+
    // $factory->new_defaultvalue = function($container) {
    //   return new StdClass();
    // }
    $bucket = new bucket_Container($factory);
    $this->assertIsA($bucket->get('DefaultValue'), 'StdClass');
  }
  function test_callback_takes_precedence_over_method() {
    $factory = new TestFactory();
    $factory->new_nodependencies = create_function('', 'return new StdClass();');
    // For PHP 5.3+
    // $factory->new_nodependencies = function($container) {
    //   return new StdClass();
    // }
    $bucket = new bucket_Container($factory);
    $this->assertIsA($bucket->get('NoDependencies'), 'StdClass');
  }
  function test_container_can_take_array_of_callbacks_as_argument() {
    $bucket = new bucket_Container(
      array(
        'DefaultValue' => create_function('', 'return new StdClass();')
      )
    );
    // For PHP 5.3+
    // $bucket = new bucket_Container(
    //   array(
    //     'DefaultValue' => function($container) {
    //       return new StdClass();
    //     }
    //   )
    // );
    $this->assertIsA($bucket->get('DefaultValue'), 'StdClass');
  }
  function test_underscore_call_is_callable() {
    $factory = new TestUnderscoreCallFactory();
    $bucket = new bucket_Container($factory);
    $this->assertIsA($bucket->get('StdClass'), 'StdClass');
    $this->assertTrue($factory->invoked);
  }
}

class TestOfBucketScope extends UnitTestCase {
  function test_a_child_scope_uses_parent_factory() {
    $factory = new TestFactory();
    $bucket = new bucket_Container($factory);
    $scope = $bucket->makeChildContainer();
    $this->assertIsA($scope->get('NoDependencies'), 'NoDependencies');
    $this->assertTrue($factory->invoked);
  }
  function test_get_on_a_child_scope_returns_same_instance_on_subsequent_calls() {
    $factory = new TestFactory();
    $bucket = new bucket_Container($factory);
    $scope = $bucket->makeChildContainer();
    $this->assertSame(
      $scope->get('NoDependencies'),
      $scope->get('NoDependencies'));
  }
  function test_get_on_a_child_scope_returns_parent_state() {
    $factory = new TestFactory();
    $bucket = new bucket_Container($factory);
    $scope = $bucket->makeChildContainer();
    $o = $bucket->get('NoDependencies');
    $this->assertSame(
      $o,
      $scope->get('NoDependencies'));
  }
  function test_parent_scope_doesnt_see_child_state() {
    $factory = new TestFactory();
    $bucket = new bucket_Container($factory);
    $scope = $bucket->makeChildContainer();
    $o = $scope->get('NoDependencies');
    $this->assertFalse($o === $bucket->get('NoDependencies'));
  }
  function test_setting_an_instance_and_getting_it_should_return_same_instance() {
    $bucket = new bucket_Container();
    $obj = new StdClass();
    $bucket->set($obj);
    $this->assertSame($bucket->get('StdClass'), $obj);
  }
}

<?php
require_once 'vendor/simpletest/simpletest/unit_tester.php';
if (realpath($_SERVER['PHP_SELF']) == __FILE__) {
  error_reporting(E_ALL | E_STRICT);
  require_once 'vendor/simpletest/simpletest/autorun.php';
}

require_once 'lib/bucket.inc.php';

class NoDependencies {}
class ExtendsNoDependencies extends NoDependencies {}

class SingleClassDependency {
  public $val;
  function __construct(NoDependencies $val) {
    $this->val = $val;
  }
}

class DefaultValue {
  public $val;
  function __construct($val = 42) {
    $this->val = $val;
  }
}

class UnTypedDependency {
  public $val;
  function __construct($val) {
    $this->val = $val;
  }
}

interface AnInterface {};

class ConcreteImplementation implements AnInterface {}

class DependsOnInterface {
  public $val;
  function __construct(AnInterface $val) {
    $this->val = $val;
  }
}

class TestFactory {
  public $invoked = false;
  function new_NoDependencies($container) {
    $this->invoked = true;
    return new NoDependencies();
  }
  function new_ConcreteImplementation($container) {
    $this->invoked = true;
    return new NoDependencies();
  }
}

class RequireUndefinedClass {
  function __construct(ClassThatDoesntExist $autoloaded) {}
}

class TriedToAutoloadException extends Exception {
  public $classname;
  function __construct($classname) {
    $this->classname = $classname;
    parent::__construct();
  }
}

function test_autoload_fail($classname) {
  throw new TriedToAutoloadException($classname);
}

class TestUnderscoreCallFactory {
  public $invoked = false;
  function __call($name, $args) {
    $this->invoked = true;
    return new StdClass();
  }
}

class TestOfBucketAutoload extends UnitTestCase {
  function setUp() {
    $this->spl_autoload_functions = spl_autoload_functions();
    if ($this->spl_autoload_functions) {
      foreach ($this->spl_autoload_functions as $fn) {
        spl_autoload_unregister($fn);
      }
    }
  }
  function tearDown() {
    if (spl_autoload_functions()) {
      foreach (spl_autoload_functions() as $fn) {
        spl_autoload_unregister($fn);
      }
    }
    if ($this->spl_autoload_functions) {
      foreach ($this->spl_autoload_functions as $fn) {
        spl_autoload_register($fn);
      }
    }
  }
  function test_undefined_class_triggers_autoload() {
    spl_autoload_register('test_autoload_fail');
    $bucket = new bucket_Container();
    $this->expectException('TriedToAutoloadException');
    $bucket->create('RequireUndefinedClass');
  }
  function test_autoload_gets_canonical_classname() {
    spl_autoload_register('test_autoload_fail');
    $bucket = new bucket_Container();
    try {
      $bucket->create('RequireUndefinedClass');
      $this->fail("Expected TriedToAutoloadException");
    } catch (TriedToAutoloadException $ex) {
      $this->assertEqual($ex->classname, 'ClassThatDoesntExist');
    }
  }
}

class TestOfBucketResolving extends UnitTestCase {
  function test_can_create_empty_container() {
    $bucket = new bucket_Container();
  }
  function test_can_create_class_with_no_dependencies() {
    $bucket = new bucket_Container();
    $this->assertIsA($bucket->create('NoDependencies'), 'NoDependencies');
  }
  function test_can_create_class_with_class_dependency() {
    $bucket = new bucket_Container();
    $o = $bucket->create('SingleClassDependency');
    $this->assertIsA($o, 'SingleClassDependency');
    $this->assertIsA($o->val, 'NoDependencies');
  }
  function test_can_create_class_with_default_value() {
    $bucket = new bucket_Container();
    $o = $bucket->create('DefaultValue');
    $this->assertIsA($o, 'DefaultValue');
    $this->assertEqual($o->val, 42);
  }
  function test_barks_on_untyped_dependency() {
    $bucket = new bucket_Container();
    try {
      $bucket->create('UnTypedDependency');
      $this->fail("Expected exception");
    } catch (bucket_CreationException $ex) {
      $this->pass("Exception caught");
    }
  }
  function test_barks_on_interface_dependency_when_unregistered() {
    $bucket = new bucket_Container();
    try {
      $bucket->create('DependsOnInterface');
      $this->fail("Expected exception");
    } catch (bucket_CreationException $ex) {
      $this->pass("Exception caught");
    }
  }
  function test_can_create_class_with_interface_dependency() {
    $bucket = new bucket_Container();
    $bucket->registerImplementation('AnInterface', 'ConcreteImplementation');
    $o = $bucket->create('DependsOnInterface');
    $this->assertIsA($o, 'DependsOnInterface');
    $this->assertIsA($o->val, 'ConcreteImplementation');
  }
  function test_can_set_different_implementation_for_concrete_class() {
    $bucket = new bucket_Container();
    $bucket->registerImplementation('NoDependencies', 'ExtendsNoDependencies');
    $o = $bucket->create('SingleClassDependency');
    $this->assertIsA($o, 'SingleClassDependency');
    $this->assertIsA($o->val, 'ExtendsNoDependencies');
  }
}

class TestOfBucketContainer extends UnitTestCase {
  function test_get_creates_new_object() {
    $bucket = new bucket_Container();
    $this->assertIsA($bucket->get('NoDependencies'), 'NoDependencies');
  }
  function test_get_returns_same_instance_on_subsequent_calls() {
    $bucket = new bucket_Container();
    $this->assertSame(
      $bucket->get('NoDependencies'),
      $bucket->get('NoDependencies'));
  }
}

class TestOfBucketFactory extends UnitTestCase {
  function test_container_delegates_to_factory_method() {
    $factory = new TestFactory();
    $bucket = new bucket_Container($factory);
    $this->assertIsA($bucket->get('NoDependencies'), 'NoDependencies');
    $this->assertTrue($factory->invoked);
  }
  function test_container_can_return_different_implementation() {
    $bucket = new bucket_Container(new TestFactory());
    $this->assertIsA($bucket->get('ConcreteImplementation'), 'NoDependencies');
  }
  function test_container_delegates_to_factory_callback() {
    $factory = new TestFactory();
    $factory->new_defaultvalue = create_function('', 'return new StdClass();');
    // For PHP 5.3+
    // $factory->new_defaultvalue = function($container) {
    //   return new StdClass();
    // }
    $bucket = new bucket_Container($factory);
    $this->assertIsA($bucket->get('DefaultValue'), 'StdClass');
  }
  function test_callback_takes_precedence_over_method() {
    $factory = new TestFactory();
    $factory->new_nodependencies = create_function('', 'return new StdClass();');
    // For PHP 5.3+
    // $factory->new_nodependencies = function($container) {
    //   return new StdClass();
    // }
    $bucket = new bucket_Container($factory);
    $this->assertIsA($bucket->get('NoDependencies'), 'StdClass');
  }
  function test_container_can_take_array_of_callbacks_as_argument() {
    $bucket = new bucket_Container(
      array(
        'DefaultValue' => create_function('', 'return new StdClass();')
      )
    );
    // For PHP 5.3+
    // $bucket = new bucket_Container(
    //   array(
    //     'DefaultValue' => function($container) {
    //       return new StdClass();
    //     }
    //   )
    // );
    $this->assertIsA($bucket->get('DefaultValue'), 'StdClass');
  }
  function test_underscore_call_is_callable() {
    $factory = new TestUnderscoreCallFactory();
    $bucket = new bucket_Container($factory);
    $this->assertIsA($bucket->get('StdClass'), 'StdClass');
    $this->assertTrue($factory->invoked);
  }
}

class TestOfBucketScope extends UnitTestCase {
  function test_a_child_scope_uses_parent_factory() {
    $factory = new TestFactory();
    $bucket = new bucket_Container($factory);
    $scope = $bucket->makeChildContainer();
    $this->assertIsA($scope->get('NoDependencies'), 'NoDependencies');
    $this->assertTrue($factory->invoked);
  }
  function test_get_on_a_child_scope_returns_same_instance_on_subsequent_calls() {
    $factory = new TestFactory();
    $bucket = new bucket_Container($factory);
    $scope = $bucket->makeChildContainer();
    $this->assertSame(
      $scope->get('NoDependencies'),
      $scope->get('NoDependencies'));
  }
  function test_get_on_a_child_scope_returns_parent_state() {
    $factory = new TestFactory();
    $bucket = new bucket_Container($factory);
    $scope = $bucket->makeChildContainer();
    $o = $bucket->get('NoDependencies');
    $this->assertSame(
      $o,
      $scope->get('NoDependencies'));
  }
  function test_parent_scope_doesnt_see_child_state() {
    $factory = new TestFactory();
    $bucket = new bucket_Container($factory);
    $scope = $bucket->makeChildContainer();
    $o = $scope->get('NoDependencies');
    $this->assertFalse($o === $bucket->get('NoDependencies'));
  }
  function test_setting_an_instance_and_getting_it_should_return_same_instance() {
    $bucket = new bucket_Container();
    $obj = new StdClass();
    $bucket->set($obj);
    $this->assertSame($bucket->get('StdClass'), $obj);
  }
}


//
// vargs.js
//
//   variable argument handling for functions taking a callback
//
// usage:
//
//   var Args = new("vargs").Constructor;
//
//   function (/* [arg1, arg2, ...][,callback] */) {
//       var args = new(Args)(arguments);
//
//       args.first;    // first argument
//       args.last;     // last argument before callback
//       args.callback; // callback argument, or an empty function
//       args.all;      // all arguments except callback
//       args.length;   // number of arguments, not including callback
//
//       args.callbackGiven() // returns true or false
//       args.at(-1)          // last argument, including callback
//       args.array           // all arguments, including callback
//   }
//
exports.Constructor = function Vargs(arguments) {
    this.array = Array.prototype.slice.call(arguments);
    this.__defineGetter__('length', function () {
        if (this.callbackGiven()) {
            return this.array.length - 1;
        } else {
            return this.array.length;
        }
    });
    this.__defineGetter__('all', function () {
        if (this.callbackGiven()) {
            return this.array.slice(0, -1);
        } else {
            return this.array;
        }
    });
    this.__defineGetter__('last', function () {
        if (typeof(this.at(-1)) === 'function') {
            return this.at(-2);
        } else {
            return this.at(-1);
        }
    });
    this.__defineGetter__('first', function () {
        return this.array[0];
    });
    this.callback = this.callbackGiven() ? this.at(-1)
                                         : function () {};
};

exports.Constructor.prototype = {
    callbackGiven: function () {
        return typeof(this.at(-1)) === 'function';
    },
    at: function (n) {
        if (n < 0) {
            return this.array[this.array.length + n];
        } else {
            return this.array[n];
        }
    }
};

exports.Constructor.last = function (args) {
    return args[args.length - 1];
};

//
// vargs.js
//
//   variable argument handling for functions taking a callback
//
// usage:
//
//   var Args = new("vargs").Constructor;
//
//   function (/* [arg1, arg2, ...][,callback] */) {
//       var args = new(Args)(arguments);
//
//       args.first;    // first argument
//       args.last;     // last argument before callback
//       args.callback; // callback argument, or an empty function
//       args.all;      // all arguments except callback
//       args.length;   // number of arguments, not including callback
//
//       args.callbackGiven() // returns true or false
//       args.at(-1)          // last argument, including callback
//       args.array           // all arguments, including callback
//   }
//
exports.Constructor = function Vargs() {
    this.array = Array.prototype.slice.call(arguments);
    this.__defineGetter__('length', function () {
        if (this.callbackGiven()) {
            return this.array.length - 1;
        } else {
            return this.array.length;
        }
    });
    this.__defineGetter__('all', function () {
        if (this.callbackGiven()) {
            return this.array.slice(0, -1);
        } else {
            return this.array;
        }
    });
    this.__defineGetter__('last', function () {
        if (typeof(this.at(-1)) === 'function') {
            return this.at(-2);
        } else {
            return this.at(-1);
        }
    });
    this.__defineGetter__('first', function () {
        return this.array[0];
    });
    this.callback = this.callbackGiven() ? this.at(-1)
                                         : function () {};
};

exports.Constructor.prototype = {
    callbackGiven: function () {
        return typeof(this.at(-1)) === 'function';
    },
    at: function (n) {
        if (n < 0) {
            return this.array[this.array.length + n];
        } else {
            return this.array[n];
        }
    }
};

exports.Constructor.last = function (args) {
    return args[args.length - 1];
};


import asyncore

from pyftpdlib import ftpserver
from ftpcloudfs.utils import smart_str
from server import RackspaceCloudAuthorizer
from multiprocessing.managers import RemoteError

class MyDTPHandler(ftpserver.DTPHandler):
    def send(self, data):
        data=smart_str(data)
        return super(MyDTPHandler, self).send(data)

    def close(self):
        if self.file_obj is not None and not self.file_obj.closed:
            try:
                self.file_obj.close()
            except Exception, e:
                msg = "Data connection error (%s)" % e
                self.cmd_channel.log(msg)
                self.cmd_channel.respond("421 " + msg)
            finally:
                self.file_obj = None

        super(MyDTPHandler, self).close()

class MyFTPHandler(ftpserver.FTPHandler):
    # don't kick off client in long time transactions
    timeout = 0
    dtp_handler = MyDTPHandler
    authorizer = RackspaceCloudAuthorizer()

    @staticmethod
    def abstracted_fs(root, cmd_channel):
        '''Get an AbstractedFs for the user logged in on the cmd_channel'''
        cffs = cmd_channel.authorizer.get_abstracted_fs(cmd_channel.username)
        cffs.init_abstracted_fs(root, cmd_channel)
        return cffs

    def process_command(self, cmd, *args, **kwargs):
        '''Flush the FS cache with every new FTP command'''
        if self.fs:
            if not self.fs.single_cache:
                self.fs.flush()
            self.fs.connection.real_ip = self.remote_ip
        super(MyFTPHandler, self).process_command(cmd, *args, **kwargs)

    def ftp_MD5(self, path):
        line = self.fs.fs2ftp(path)
        try:
            md5_checksum = self.run_as_current_user(self.fs.md5, path)
        except OSError, err:
            why = ftpserver._strerror(err)
            self.respond('550 %s.' % why)
        else:
            msg = md5_checksum.upper()
            self.respond('251 "%s" %s' % (line.replace('"', '""'), msg))

    def handle(self):
        """Track the ip and check max cons per ip (if needed)"""
        if self.server.max_cons_per_ip and self.remote_ip and self.shared_ip_map != None:
            try:
                self.shared_lock.acquire()
                count = self.shared_ip_map.get(self.remote_ip, 0)
                self.shared_ip_map[self.remote_ip] = count + 1
                self.shared_lock.release()
            except RemoteError, e:
                self.logerror("Connection tracking failed: %s" % e)

            self.logline("Connection track: %s -> %s" % (self.remote_ip, count+1))

            if self.shared_ip_map[self.remote_ip] > self.server.max_cons_per_ip:
                self.handle_max_cons_per_ip()
                return

            self.logline("Connected, shared ip map: %s" % self.shared_ip_map)

        super(MyFTPHandler, self).handle()

    def close(self):
        """Remove the ip from the shared map before calling close"""
        if not self._closed and self.server.max_cons_per_ip and self.shared_ip_map != None:
            try:
                self.shared_lock.acquire()
                if self.remote_ip in self.shared_ip_map:
                    self.shared_ip_map[self.remote_ip] -= 1
                    if self.shared_ip_map[self.remote_ip] <= 0:
                        del self.shared_ip_map[self.remote_ip]
                self.shared_lock.release()
            except RemoteError, e:
                self.logerror("Connection tracking cleanup failed: %s" % e)

            self.logline("Disconnected, shared ip map: %s" % self.shared_ip_map)

        super(MyFTPHandler, self).close()

    def log_cmd(self, cmd, arg, respcode, respstr):
        """
        We use the same format pyftpdlib is using, but we want to log more commands.
        """
        if cmd in ("ABOR", "APPE", "DELE", "RMD", "RNFR", "RNTO", "RETR", "STOR", "MKD"):
            line = '"%s" %s' % (' '.join([cmd, str(arg)]).strip(), respcode)
            self.log(line)

import asyncore

from pyftpdlib import ftpserver
from ftpcloudfs.utils import smart_str
from server import RackspaceCloudAuthorizer
from multiprocessing.managers import RemoteError

class MyDTPHandler(ftpserver.DTPHandler):
    def send(self, data):
        data=smart_str(data)
        return super(MyDTPHandler, self).send(data)

    def close(self):
        if self.file_obj is not None and not self.file_obj.closed:
            try:
                self.file_obj.close()
            except Exception, e:
                msg = "Data connection error (%s)" % e
                self.cmd_channel.log(msg)
                self.cmd_channel.respond("421 " + msg)
            finally:
                self.file_obj = None

        super(MyDTPHandler, self).close()

class MyFTPHandler(ftpserver.FTPHandler):
    # don't kick off client in long time transactions
    timeout = 0
    dtp_handler = MyDTPHandler
    authorizer = RackspaceCloudAuthorizer()
    max_cons_per_ip = 0

    @staticmethod
    def abstracted_fs(root, cmd_channel):
        '''Get an AbstractedFs for the user logged in on the cmd_channel'''
        cffs = cmd_channel.authorizer.get_abstracted_fs(cmd_channel.username)
        cffs.init_abstracted_fs(root, cmd_channel)
        return cffs

    def process_command(self, cmd, *args, **kwargs):
        '''Flush the FS cache with every new FTP command'''
        if self.fs:
            if not self.fs.single_cache:
                self.fs.flush()
            self.fs.connection.real_ip = self.remote_ip
        super(MyFTPHandler, self).process_command(cmd, *args, **kwargs)

    def ftp_MD5(self, path):
        line = self.fs.fs2ftp(path)
        try:
            md5_checksum = self.run_as_current_user(self.fs.md5, path)
        except OSError, err:
            why = ftpserver._strerror(err)
            self.respond('550 %s.' % why)
        else:
            msg = md5_checksum.upper()
            self.respond('251 "%s" %s' % (line.replace('"', '""'), msg))

    def handle(self):
        """Track the ip and check max cons per ip (if needed)"""

        if self.max_cons_per_ip and self.remote_ip and self.shared_ip_map != None:
            count = 0
            try:
                self.shared_lock.acquire()
                count = self.shared_ip_map.get(self.remote_ip, 0) + 1
                self.shared_ip_map[self.remote_ip] = count

                self.logline("Connection track: %s -> %s" % (self.remote_ip, count))
            except RemoteError, e:
                self.logerror("Connection tracking failed: %s" % e)
            finally:
                self.shared_lock.release()

            if count > self.max_cons_per_ip:
                self.handle_max_cons_per_ip()
                return

            self.logline("Connected, shared ip map: %s" % self.shared_ip_map)

        super(MyFTPHandler, self).handle()

    def close(self):
        """Remove the ip from the shared map before calling close"""
        if not self._closed and self.max_cons_per_ip and self.shared_ip_map != None:
            try:
                self.shared_lock.acquire()
                if self.remote_ip in self.shared_ip_map:
                    self.shared_ip_map[self.remote_ip] -= 1
                    if self.shared_ip_map[self.remote_ip] <= 0:
                        del self.shared_ip_map[self.remote_ip]
            except RemoteError, e:
                self.logerror("Connection tracking cleanup failed: %s" % e)
            finally:
                self.shared_lock.release()

            self.logline("Disconnected, shared ip map: %s" % self.shared_ip_map)

        super(MyFTPHandler, self).close()

    def log_cmd(self, cmd, arg, respcode, respstr):
        """
        We use the same format pyftpdlib is using, but we want to log more commands.
        """
        if cmd in ("ABOR", "APPE", "DELE", "RMD", "RNFR", "RNTO", "RETR", "STOR", "MKD"):
            line = '"%s" %s' % (' '.join([cmd, str(arg)]).strip(), respcode)
            self.log(line)


import logging

from django import forms
from django.utils.translation import ugettext_lazy as _
from models import Article, Tag

log = logging.getLogger('articles.forms')

def tag(name):
    """Returns a Tag object for the given name"""

    slug = Tag.clean_tag(name)

    log.debug('Looking for Tag with slug "%s"...' % (slug,))
    t, created = Tag.objects.get_or_create(slug=slug, defaults={'name': name})
    log.debug('Found Tag %s. Name: %s Slug: %s Created: %s' % (t.pk, t.name, t.slug, created))

    if not t.name:
        t.name = name
        t.save()

    return t

class ArticleAdminForm(forms.ModelForm):
    tags = forms.CharField(initial='', required=False,
                           widget=forms.TextInput(attrs={'size': 100}),
                           help_text=_('Words that describe this article'))

    def __init__(self, *args, **kwargs):
        """Sets the list of tags to be a string"""

        instance = kwargs.get('instance', None)
        if instance:
            init = kwargs.get('initial', {})
            init['tags'] = ' '.join([t.name for t in instance.tags.all()])
            kwargs['initial'] = init

        super(ArticleAdminForm, self).__init__(*args, **kwargs)

    def clean_tags(self):
        """Turns the string of tags into a list"""

        tags = [tag(t.strip()) for t in self.cleaned_data['tags'].split() if len(t.strip())]

        log.debug('Tagging Article %s with: %s' % (self.cleaned_data['title'], tags))
        self.cleaned_data['tags'] = tags
        return self.cleaned_data['tags']

    class Meta:
        model = Article

    class Media:
        css = {
            'all': ('articles/css/jquery.autocomplete.css',),
        }
        js = (
            'articles/js/jquery-1.4.1.min.js',
            'articles/js/jquery.bgiframe.min.js',
            'articles/js/jquery.autocomplete.pack.js',
            'articles/js/tag_autocomplete.js',
        )

import logging

from django import forms
from django.utils.translation import ugettext_lazy as _
from models import Article, Tag

log = logging.getLogger('articles.forms')

def tag(name):
    """Returns a Tag object for the given name"""

    slug = Tag.clean_tag(name)

    log.debug('Looking for Tag with slug "%s"...' % (slug,))
    t, created = Tag.objects.get_or_create(slug=slug, defaults={'name': name})
    log.debug('Found Tag %s. Name: %s Slug: %s Created: %s' % (t.pk, t.name, t.slug, created))

    if not t.name:
        t.name = name
        t.save()

    return t

class ArticleAdminForm(forms.ModelForm):
    tags = forms.CharField(initial='', required=False,
                           widget=forms.TextInput(attrs={'size': 100}),
                           help_text=_('Words that describe this article'))

    def __init__(self, *args, **kwargs):
        """Sets the list of tags to be a string"""

        instance = kwargs.get('instance', None)
        if instance:
            init = kwargs.get('initial', {})
            init['tags'] = ' '.join([t.name for t in instance.tags.all()])
            kwargs['initial'] = init

        super(ArticleAdminForm, self).__init__(*args, **kwargs)

    def clean_tags(self):
        """Turns the string of tags into a list"""

        tags = [tag(t.strip()) for t in self.cleaned_data['tags'].split() if len(t.strip())]

        log.debug('Tagging Article %s with: %s' % (self.cleaned_data['title'], tags))
        self.cleaned_data['tags'] = tags
        return self.cleaned_data['tags']

    def save(self, *args, **kwargs):
        """Remove any old tags that may have been set that we no longer need"""
        if self.instance.pk:
            self.instance.tags.clear()
        return super(ArticleAdminForm, self).save(*args, **kwargs)

    class Meta:
        model = Article

    class Media:
        css = {
            'all': ('articles/css/jquery.autocomplete.css',),
        }
        js = (
            'articles/js/jquery-1.4.1.min.js',
            'articles/js/jquery.bgiframe.min.js',
            'articles/js/jquery.autocomplete.pack.js',
            'articles/js/tag_autocomplete.js',
        )