<commit_before>var utils   = require('./utils'),
    helpers = require('./helpers'),
    custom  = require('./custom'),
    connect = require('express/node_modules/connect');

// Custom middleware used by the application.
module.exports = {
  // Transparently handle JSONP. Although this does require wrapping the
  // Request#send() method which is a bit nasty. Other alternative is to
  // always pass the response on and include this middleware after the
  // routes.
  jsonp: function () {
    return function (req, res, next) {
      var _send = res.send;
      res.send = function (body) {
        var callback = req.param('callback'),
            isJSONP  = res.get('Content-Type') === 'application/json' && callback;

        if (body && req.method !== 'HEAD' && isJSONP) {
          res.contentType('js');
          body = callback + '(' + body.toString().trim() + ');';
        }
        _send.call(this, body);
      };
      next();
    };
  },
  // Check for ajax requests and set the Request/Response#ajax flag to true.
  ajax: function () {
    return function (req, res, next) {
      req.ajax = res.ajax = utils.isAjax(req);

      // if ((req.get('X-Stream') || '').toLowerCase() === 'true') {
      //   req.ajax = false;
      // }

      next();
    };
  },
  // Add relevant CORS headers for the application.
  cors: function () {
    return function (req, res, next) {
      if (req.method === 'OPTIONS' || (req.method === 'GET' && utils.isAjax(req))) {
        res.header({
          'Access-Control-Allow-Origin':  '*',
          'Access-Control-Allow-Headers': 'X-Requested-With, Accept, Cache-Control, Origin, Last-Event-ID, X-Stream'
        });
        req.cors = true;
      }

      next();
    };
  },
  flash: function () {
    function flash(key, message) {
      var cache = this.session.flash = this.session.flash || {},
          value;

      if (arguments.length === 2) {
        cache[key] = message;
        return this;
      } else if (arguments.length === 1) {
        value = cache[key];
        delete cache[key];
        return value;
      }

      this.session.flash = {};
      return cache;
    }

    return function (req, res, next) {
      req.flash = res.flash = flash.bind(req);

      req.flash.INFO = 'info';
      req.flash.ERROR = 'error';

      next();
    };
  },
  // Redirect urls with trailing slashes to their non-trailing counterpart,
  // requires the "strict routing" setting to be false.
  noslashes: function () {
    return function (req, res, next) {
      if (req.path !== '/' && req.originalUrl.slice(-1) === '/') {
        return res.redirect(301, req.originalUrl.slice(0, -1));
      }
      next();
    };
  },
  // monkey patch for express' own csrf method, so we can ignore specific url patterns
  csrf: function (options) {
    var ignore = options.ignore || [],
        csrf = connect.csrf(options),
        always = {OPTIONS: 1, GET: 1, HEAD: 1};

    return function (req, res, next) {
      if (always[req.method] || ignore.indexOf(req.url) === -1 && !req.cors) {
        return csrf(req, res, next);
      } else {
        next();
      }
    };
  },
  // Checks for a subdomain in the current url, if found it sets the
  // req.subdomain property. This supports existing behaviour that allows
  // subdomains to load custom config files.
  subdomain: function (app) {
    return function (req, res, next) {
      var host  = req.header('Host', ''),
          parts = host.split('.');

      if (parts.length > 2) {
        req.subdomain = parts.slice(0, -2).join('.');
      }
      next();
    };
  }
};
<commit_msg>Reduce allowed headers<commit_after>var utils   = require('./utils'),
    helpers = require('./helpers'),
    custom  = require('./custom'),
    connect = require('express/node_modules/connect');

// Custom middleware used by the application.
module.exports = {
  // Transparently handle JSONP. Although this does require wrapping the
  // Request#send() method which is a bit nasty. Other alternative is to
  // always pass the response on and include this middleware after the
  // routes.
  jsonp: function () {
    return function (req, res, next) {
      var _send = res.send;
      res.send = function (body) {
        var callback = req.param('callback'),
            isJSONP  = res.get('Content-Type') === 'application/json' && callback;

        if (body && req.method !== 'HEAD' && isJSONP) {
          res.contentType('js');
          body = callback + '(' + body.toString().trim() + ');';
        }
        _send.call(this, body);
      };
      next();
    };
  },
  // Check for ajax requests and set the Request/Response#ajax flag to true.
  ajax: function () {
    return function (req, res, next) {
      req.ajax = res.ajax = utils.isAjax(req);

      // if ((req.get('X-Stream') || '').toLowerCase() === 'true') {
      //   req.ajax = false;
      // }

      next();
    };
  },
  // Add relevant CORS headers for the application.
  cors: function () {
    return function (req, res, next) {
      if (req.method === 'OPTIONS' || (req.method === 'GET' && utils.isAjax(req))) {
        res.header({
          'Access-Control-Allow-Origin':  '*',
          'Access-Control-Allow-Headers': 'X-Requested-With, Cache-Control, Last-Event-ID'
        });
        req.cors = true;
      }

      next();
    };
  },
  flash: function () {
    function flash(key, message) {
      var cache = this.session.flash = this.session.flash || {},
          value;

      if (arguments.length === 2) {
        cache[key] = message;
        return this;
      } else if (arguments.length === 1) {
        value = cache[key];
        delete cache[key];
        return value;
      }

      this.session.flash = {};
      return cache;
    }

    return function (req, res, next) {
      req.flash = res.flash = flash.bind(req);

      req.flash.INFO = 'info';
      req.flash.ERROR = 'error';

      next();
    };
  },
  // Redirect urls with trailing slashes to their non-trailing counterpart,
  // requires the "strict routing" setting to be false.
  noslashes: function () {
    return function (req, res, next) {
      if (req.path !== '/' && req.originalUrl.slice(-1) === '/') {
        return res.redirect(301, req.originalUrl.slice(0, -1));
      }
      next();
    };
  },
  // monkey patch for express' own csrf method, so we can ignore specific url patterns
  csrf: function (options) {
    var ignore = options.ignore || [],
        csrf = connect.csrf(options),
        always = {OPTIONS: 1, GET: 1, HEAD: 1};

    return function (req, res, next) {
      if (always[req.method] || ignore.indexOf(req.url) === -1 && !req.cors) {
        return csrf(req, res, next);
      } else {
        next();
      }
    };
  },
  // Checks for a subdomain in the current url, if found it sets the
  // req.subdomain property. This supports existing behaviour that allows
  // subdomains to load custom config files.
  subdomain: function (app) {
    return function (req, res, next) {
      var host  = req.header('Host', ''),
          parts = host.split('.');

      if (parts.length > 2) {
        req.subdomain = parts.slice(0, -2).join('.');
      }
      next();
    };
  }
};
<|endoftext|>


<commit_before>module.exports = function middlewareConstructor(env) {

  var metrics = require('./metrics')(env),
      utils = require("./utils");

  return {
    /**
     * The operation for the / route is special,
     * and never an edit or remix.
     */
    setNewPageOperation: function(req, res, next) {
      req.body.pageOperation = "create";
      next();
    },

    /**
     * By default, the publish operation is to create a new
     * page. Later functions can override this behaviour.
     */
    setDefaultPublishOperation: function(req, res, next) {
      req.body.pageOperation = "remix";
      next();
    },

    /**
     * Override the default publication operation to act
     * as an update, rather than a create. This will lead
     * to old data being overwritten upon publication.
     */
    setPublishAsUpdate: function(req, res, next) {
      req.body.pageOperation = "edit";
      next();
    },

    /**
     * Check whether the requesting user is authenticated through Persona.
     */
    checkForAuth: function(req, res, next) {
      if (!req.session.email || !req.session.username) {
        return next(new Error("please log in first"));
      }
      next();
    },

    /**
     * Check to see whether a page request is actually for some page.
     */
    requestForId: function(req, res, next) {
      if(!req.params.id) {
        return next(new Error("request did not point to a project"));
      }
      next();
    },

    /**
     * Check to see if a publish attempt actually has data for publishing.
     */
    checkForPublishData: function(req, res, next) {
      if(!req.body.html || req.body.html.trim() === "") {
        return next(new Error("no data to publish"));
      }
      next();
    },

    /**
     * Ensure a publish has metadata. If not default it to an empty object.
     */
    ensureMetaData: function(req, res, next) {
      if(!req.body.metaData) {
        req.body.metaData = {};
      }
      next();
    },

    /**
     * Sanitize metadata so that there's no raw HTML in it
     */
    sanitizeMetaData: function(req, res, next) {
      var escapeHTML = function(content) {
            return content.replace(/</g, "<").replace(/>/g, ">");
          },
          metaData = req.body.metaData,
          prop;
      for(prop in metaData) {
        if(metaData.hasOwnProperty(prop)) {
          metaData[prop] = escapeHTML(metaData[prop]);
        }
      }
      next();
    },

    /**
     * Ensure we're safe to do an edit, if not, force a remix.
     */
    checkPageOperation: function(db) {
      return function(req, res, next) {
        var originalId = req.body.origin;
        // Ensure we are doing an edit on an existing project.
        if (!originalId) {
          return next();
        }

        // Verify that the currently logged in user owns
        // this page, otherwise they might try to update
        // a non-existent page when they hit "publish".
        db.find(originalId, function(err, result) {
          if(err) {
            return next(err);
          }

          // We own this page, so an edit is safe.
          if (req.body.pageOperation === "edit" && result.userid === req.session.email) {
            // We need to know if an edit changed the title,
            // so we can update the old project by the old url.
            if (req.body.metaData.title !== result.title) {
              req.oldUrl = result.url;
            }
            return next();
          }

          // Otherwise, we don't own this page. Go to a remix instead.
          req.body.remixedFrom = result.url;
          req.body.pageOperation = "remix";
          next();
        });
      };
    },

    /**
     * Publish a page to the database. If it's a publish by
     * the owning user, update. Otherwise, insert.
     */
    saveData: function(db, hostName) {
      return function(req, res, next) {
        if (req.body.metaData.title) {
          req.pageTitle = utils.slugify(req.body.metaData.title);
        } else {
          req.pageTitle = "";
        }

        var options = {
          edit: (req.body.pageOperation === "edit"),
          origin: req.body.origin,
          remixedFrom: req.body.remixedFrom,
          rawData: req.body.html,
          title: req.pageTitle,
          userid: req.session.email
        };

        db.write(options, function(err, result) {
          if (err) {
            metrics.increment('project.save.error');
          } else {
            req.publishId = result.id;
            metrics.increment('project.save.success');
          }
          next(err);
        });
      };
    },

    /**
     * Update the database to store the URL created from S3
     */
    saveUrl: function(db) {
      return function(req, res, next) {
        var options = {
          id: req.publishId,
          userid: req.session.email,
          url: req.publishedUrl
        };
        db.updateUrl(options, function(err) {
          next(err);
        });
      };
    },

    /**
     * Find the make id of the project this was remixed from
     */
    getRemixedFrom: function(db, make) {
      return function(req, res, next) {
        db.find(req.publishId, function(err, result) {
          if (err) {
            return next(err);
          }
          // This means we don't have a remix to worry about
          if (!result.remixedFrom) {
            return next();
          }
          make.search({url: result.remixedFrom}, function(err, makes) {
            if (err) {
              return next(err);
            }
            if (makes.length === 1) {
              req.body.remixedFrom = makes[0]._id;
            }
            next();
          });
        });
      };
    },

    rewritePublishId: function(db) {
      return function(req, res, next) {
        // If the user hasn't defined a title, just use the publishId as-is
        if (!req.pageTitle) {
          req.pageTitle = req.publishId;
          return next();
        }

        // is this an edit or supposed to be a new page?
        var edit = (req.body.pageOperation === "edit");

        db.count({
          userid: req.session.email,
          title: req.pageTitle
        }, function(err, count) {
          if (err) {
            return next(err);
          }

          if (!edit && count > 1) {
            return next('You already have a page titled "' + req.pageTitle +
                        '" (you will have to change the text in the <title> element on your page)');
          }

          next();
        });
      };
    },

    generateUrls: function(appName, s3Url, domain) {
      var url = require("url"),
          knox = require("knox"),
          s3 = knox.createClient(s3Url);

      return function(req, res, next) {
        var subdomain = req.session.username,
            path = "/" + appName + "/" + req.pageTitle;

        // Used by s3
        req.publishLocation = "/" + subdomain + path;
        req.s3Url = s3.url(req.publishLocation);

        // Used for make API if USER_SUBDOMAIN exists
        if (domain) {
          domain = url.parse(domain);
          req.customUrl = domain.protocol + "//" + subdomain + "." + domain.host + path;
        }

        next();
      };
    },

    finalizeProject: function(nunjucksEnv, env) {
      var generateMetaData = function(metaData, url) {
        var content = "",
            metaTags = [{
              "name": "og:url",
              "content": url
            },{
              "name": "og:updated_time",
              "content": (new Date()).toString()
            },{
              "name": "og:site_name",
              "content": "Mozilla Webmaker"
            }];

        for(var tag in metaData ) {
          if (metaData.hasOwnProperty(tag)) {
            content = metaData[ tag ];
            if (Array.isArray(content)) {
              content = content.join();
            } else if (typeof content !== "string") {
              content = "";
            }

            metaTags.push({
              "name": "og:" + tag,
              content: content
            });
          }
        }
        return metaTags;
      };

      var bodyRegExp = /]*)>/;

      return function(req, res, next) {
        var htmlData = req.body.html,
            hostname = env.get("HOSTNAME"),
            metaData = req.body.metaData,
            remixUrl = hostname + "/project/" + req.publishId + "/remix",
            detailsTemplate = nunjucksEnv.getTemplate('details.html'),
            metaTagTemplate = nunjucksEnv.getTemplate("metaTags.html"),
            googleTemplate = nunjucksEnv.getTemplate("googleanalytics.html"),
            url = req.customUrl || req.s3Url,

            // prevent index robots from following outbound links
            noFollow = '<meta name="robots" content="nofollow">\n';

            // ensure iframe links open in their own tab/window
            baseTag = '<base target="_blank">\n',

            // OpenGraph information
            metaTagsBlock = metaTagTemplate.render({
              metaTags: generateMetaData(metaData, hostname + "/project/" + req.publishId)
            }),

            // google analytics
            googleAnalytics = googleTemplate.render(res.app.locals);

            // embed shell
            embedShell = detailsTemplate.render({
              HOSTNAME: hostname,
              URL: escape( url ),
              AUDIENCE: env.get( "AUDIENCE" ),
              REMIX_URL: remixUrl,
              THIMBLE_PROJECT: req.s3Url + "_",
              TITLE: metaData.title
            });

        // combine all the content blocks.
        embedShell = embedShell.replace("</head", metaTagsBlock + googleAnalytics + "</head");
        req.remixUrl = remixUrl;
        req.body.embedShell = embedShell;
        req.body.finalizedHTML = htmlData.replace("</head", baseTag + "</head");
        next();
      };
    },

    /**
     * Publish a page to S3. If it's a publish by
     * the owning user, this effects an update. Otherwise,
     * this will create a new S3 object (=page).
     */
    publishData: function(options) {
      // NOTE: workaround until https://github.com/LearnBoost/knox/issues/194 is addressed.
      //       this line prevents knox from forming url-validation-failing S3 URLs.
      if(!options.port) { delete options.port; }

      var knox = require("knox"),
          s3 = knox.createClient(options);

      return function(req, res, next) {
        var userId = req.session.username,
            data = req.body.finalizedHTML,
            headers = {
              'x-amz-acl': 'public-read',
              'Content-Length': Buffer.byteLength(data,'utf8'),
              'Content-Type': 'text/html;charset=UTF-8'
            };

        // TODO: proper mapping for old->new ids.
        // See: https://bugzilla.mozilla.org/show_bug.cgi?id=862911
        var location = req.publishLocation,
            // FIXME: Plan for S3 being down. This is not the ideal error handling,
            //        but is a working stub in lieu of a proper solution.
            // See: https://bugzilla.mozilla.org/show_bug.cgi?id=865738
            s3PublishError = new Error("There was a problem publishing the page. Your page has been saved"+
                                       " with id "+req.publishId+", so you can edit it, but it could not be"+
                                       " published to the web."),
            s3Error = new Error("failure during publish step (error "+res.statusCode+")");

        // write data to S3
        s3.put(location + "_", headers)
          .on("error", function(err) {
            next(s3PublishError);
          })
          .on("response", function(res) {
            if (res.statusCode === 200) {
              var embedShell = req.body.embedShell;
              headers = {
                'x-amz-acl': 'public-read',
                'Content-Length': Buffer.byteLength(embedShell,'utf8'),
                'Content-Type': 'text/html;charset=UTF-8'
              };
              s3.put(location, headers)
                .on("error", function(err) {
                  next(s3PublishError);
                })
                .on("response", function(res) {
                  if (res.statusCode === 200) {
                    req.publishedUrl = s3.url(location);
                    metrics.increment('project.publish.success');

                    // publish .../edit and .../remix redirecting routes to S3 as well.
                    var target = req.remixUrl.replace(/\/remix$/,'');
                    ["edit","remix"].forEach(function(suffix) {
                      var thimbletarget = target + '/'+ suffix;
                      var s3target = location + '/' + suffix;

                      // This currently uses meta-refresh, but should be switched over to
                      // native S3 redirect headers instead, once we figure out how.
                      // SEE: https://bugzilla.mozilla.org/show_bug.cgi?id=876763
                      var redirect = "<!doctype html><meta http-equiv='refresh' content='0; url="+thimbletarget+"'>";

                      headers = {
                        'x-amz-acl': 'public-read',
                        'Content-Length': Buffer.byteLength(redirect,'utf8'),
                        'Content-Type': 'text/html;charset=UTF-8'
                      };

                      // NOTE: failure to set up the /edit and /remix links
                      //       should not actually break publishing, so we
                      //       don't catch on(error) or on(response).
                      s3.put(s3target, headers).end(redirect);
                    });
                    next();
                  } else {
                    metrics.increment('project.publish.error');
                    next(s3Error);
                  }
                })
                .end(embedShell);
            } else {
              metrics.increment('project.publish.error');
              next(s3Error);
            }
          })
          .end(data);
      };
    },
    /**
     * Turn the S3 URL into a user subdomain
     */
    rewriteUrl: function(req, res, next) {
      if (req.customUrl) {
        req.publishedUrl = req.customUrl;
      }
      next();
    },

    /**
     * Publish a page to the makeAPI. If it's "our" page,
     * update, otherwise, create.
     */
    publishMake: function(make) {
      return function(req, res, next) {
        var metaData = req.body.metaData,
            options = {
              thumbnail: metaData.thumbnail,
              contentType: "application/x-thimble",
              title: metaData.title || "",
              description: metaData.description || "",
              author: metaData.author || "",
              locale: metaData.locale || "",
              email: req.session.email,
              url: req.publishedUrl,
              remixedFrom: req.body.remixedFrom,
              remixUrl: req.remixUrl,
              tags: metaData.tags ? metaData.tags.split(",") : []
            };

        function finalizePublishMake(err, result) {
          if (err) {
            metrics.increment('makeapi.publish.error');
            next(err);
          } else {
            metrics.increment('makeapi.publish.success');
            next();
          }
        }

        // Publish the make to the makeapi.
        // If it exists and we own it, update it.
        // Otherwise create a new one.
        make.search({
          email: req.session.email,
          url: req.oldUrl || req.publishedUrl
        }, function(err, results) {
          if (err) {
            return finalizePublishMake(err);
          }

          var result = results[0];
          if (result) {
            make.update(result.id, options, finalizePublishMake);
          } else {
            make.create(options, finalizePublishMake);
          }
        });
      };
    },

    /**
     * Unpublish (delete/remove) a project.
     */
    deleteProject: function(databaseAPI) {
      return function(req, res, next) {
        databaseAPI.destroy(req.requestId, function(err, project) {
          if(err) {
            next(err);
          }
          res.json({"status": 200});
        });
      };
    }
  };
};
<commit_msg>Fix bug 910905 - Send contenturl to MakeAPI<commit_after>module.exports = function middlewareConstructor(env) {

  var metrics = require('./metrics')(env),
      utils = require("./utils");

  return {
    /**
     * The operation for the / route is special,
     * and never an edit or remix.
     */
    setNewPageOperation: function(req, res, next) {
      req.body.pageOperation = "create";
      next();
    },

    /**
     * By default, the publish operation is to create a new
     * page. Later functions can override this behaviour.
     */
    setDefaultPublishOperation: function(req, res, next) {
      req.body.pageOperation = "remix";
      next();
    },

    /**
     * Override the default publication operation to act
     * as an update, rather than a create. This will lead
     * to old data being overwritten upon publication.
     */
    setPublishAsUpdate: function(req, res, next) {
      req.body.pageOperation = "edit";
      next();
    },

    /**
     * Check whether the requesting user is authenticated through Persona.
     */
    checkForAuth: function(req, res, next) {
      if (!req.session.email || !req.session.username) {
        return next(new Error("please log in first"));
      }
      next();
    },

    /**
     * Check to see whether a page request is actually for some page.
     */
    requestForId: function(req, res, next) {
      if(!req.params.id) {
        return next(new Error("request did not point to a project"));
      }
      next();
    },

    /**
     * Check to see if a publish attempt actually has data for publishing.
     */
    checkForPublishData: function(req, res, next) {
      if(!req.body.html || req.body.html.trim() === "") {
        return next(new Error("no data to publish"));
      }
      next();
    },

    /**
     * Ensure a publish has metadata. If not default it to an empty object.
     */
    ensureMetaData: function(req, res, next) {
      if(!req.body.metaData) {
        req.body.metaData = {};
      }
      next();
    },

    /**
     * Sanitize metadata so that there's no raw HTML in it
     */
    sanitizeMetaData: function(req, res, next) {
      var escapeHTML = function(content) {
            return content.replace(/</g, "<").replace(/>/g, ">");
          },
          metaData = req.body.metaData,
          prop;
      for(prop in metaData) {
        if(metaData.hasOwnProperty(prop)) {
          metaData[prop] = escapeHTML(metaData[prop]);
        }
      }
      next();
    },

    /**
     * Ensure we're safe to do an edit, if not, force a remix.
     */
    checkPageOperation: function(db) {
      return function(req, res, next) {
        var originalId = req.body.origin;
        // Ensure we are doing an edit on an existing project.
        if (!originalId) {
          return next();
        }

        // Verify that the currently logged in user owns
        // this page, otherwise they might try to update
        // a non-existent page when they hit "publish".
        db.find(originalId, function(err, result) {
          if(err) {
            return next(err);
          }

          // We own this page, so an edit is safe.
          if (req.body.pageOperation === "edit" && result.userid === req.session.email) {
            // We need to know if an edit changed the title,
            // so we can update the old project by the old url.
            if (req.body.metaData.title !== result.title) {
              req.oldUrl = result.url;
            }
            return next();
          }

          // Otherwise, we don't own this page. Go to a remix instead.
          req.body.remixedFrom = result.url;
          req.body.pageOperation = "remix";
          next();
        });
      };
    },

    /**
     * Publish a page to the database. If it's a publish by
     * the owning user, update. Otherwise, insert.
     */
    saveData: function(db, hostName) {
      return function(req, res, next) {
        if (req.body.metaData.title) {
          req.pageTitle = utils.slugify(req.body.metaData.title);
        } else {
          req.pageTitle = "";
        }

        var options = {
          edit: (req.body.pageOperation === "edit"),
          origin: req.body.origin,
          remixedFrom: req.body.remixedFrom,
          rawData: req.body.html,
          title: req.pageTitle,
          userid: req.session.email
        };

        db.write(options, function(err, result) {
          if (err) {
            metrics.increment('project.save.error');
          } else {
            req.publishId = result.id;
            metrics.increment('project.save.success');
          }
          next(err);
        });
      };
    },

    /**
     * Update the database to store the URL created from S3
     */
    saveUrl: function(db) {
      return function(req, res, next) {
        var options = {
          id: req.publishId,
          userid: req.session.email,
          url: req.publishedUrl
        };
        db.updateUrl(options, function(err) {
          next(err);
        });
      };
    },

    /**
     * Find the make id of the project this was remixed from
     */
    getRemixedFrom: function(db, make) {
      return function(req, res, next) {
        db.find(req.publishId, function(err, result) {
          if (err) {
            return next(err);
          }
          // This means we don't have a remix to worry about
          if (!result.remixedFrom) {
            return next();
          }
          make.search({url: result.remixedFrom}, function(err, makes) {
            if (err) {
              return next(err);
            }
            if (makes.length === 1) {
              req.body.remixedFrom = makes[0]._id;
            }
            next();
          });
        });
      };
    },

    rewritePublishId: function(db) {
      return function(req, res, next) {
        // If the user hasn't defined a title, just use the publishId as-is
        if (!req.pageTitle) {
          req.pageTitle = req.publishId;
          return next();
        }

        // is this an edit or supposed to be a new page?
        var edit = (req.body.pageOperation === "edit");

        db.count({
          userid: req.session.email,
          title: req.pageTitle
        }, function(err, count) {
          if (err) {
            return next(err);
          }

          if (!edit && count > 1) {
            return next('You already have a page titled "' + req.pageTitle +
                        '" (you will have to change the text in the <title> element on your page)');
          }

          next();
        });
      };
    },

    generateUrls: function(appName, s3Url, domain) {
      var url = require("url"),
          knox = require("knox"),
          s3 = knox.createClient(s3Url);

      return function(req, res, next) {
        var subdomain = req.session.username,
            path = "/" + appName + "/" + req.pageTitle;

        // Used by s3
        req.publishLocation = "/" + subdomain + path;
        req.s3Url = s3.url(req.publishLocation);

        // Used for make API if USER_SUBDOMAIN exists
        if (domain) {
          domain = url.parse(domain);
          req.customUrl = domain.protocol + "//" + subdomain + "." + domain.host + path;
        }

        next();
      };
    },

    finalizeProject: function(nunjucksEnv, env) {
      var generateMetaData = function(metaData, url) {
        var content = "",
            metaTags = [{
              "name": "og:url",
              "content": url
            },{
              "name": "og:updated_time",
              "content": (new Date()).toString()
            },{
              "name": "og:site_name",
              "content": "Mozilla Webmaker"
            }];

        for(var tag in metaData ) {
          if (metaData.hasOwnProperty(tag)) {
            content = metaData[ tag ];
            if (Array.isArray(content)) {
              content = content.join();
            } else if (typeof content !== "string") {
              content = "";
            }

            metaTags.push({
              "name": "og:" + tag,
              content: content
            });
          }
        }
        return metaTags;
      };

      var bodyRegExp = /]*)>/;

      return function(req, res, next) {
        var htmlData = req.body.html,
            hostname = env.get("HOSTNAME"),
            metaData = req.body.metaData,
            remixUrl = hostname + "/project/" + req.publishId + "/remix",
            detailsTemplate = nunjucksEnv.getTemplate('details.html'),
            metaTagTemplate = nunjucksEnv.getTemplate("metaTags.html"),
            googleTemplate = nunjucksEnv.getTemplate("googleanalytics.html"),
            url = req.customUrl || req.s3Url,

            // prevent index robots from following outbound links
            noFollow = '<meta name="robots" content="nofollow">\n';

            // ensure iframe links open in their own tab/window
            baseTag = '<base target="_blank">\n',

            // OpenGraph information
            metaTagsBlock = metaTagTemplate.render({
              metaTags: generateMetaData(metaData, hostname + "/project/" + req.publishId)
            }),

            // google analytics
            googleAnalytics = googleTemplate.render(res.app.locals);

            // embed shell
            embedShell = detailsTemplate.render({
              HOSTNAME: hostname,
              URL: escape( url ),
              AUDIENCE: env.get( "AUDIENCE" ),
              REMIX_URL: remixUrl,
              THIMBLE_PROJECT: req.s3Url + "_",
              TITLE: metaData.title
            });

        // combine all the content blocks.
        embedShell = embedShell.replace("</head", metaTagsBlock + googleAnalytics + "</head");
        req.remixUrl = remixUrl;
        req.body.embedShell = embedShell;
        req.body.finalizedHTML = htmlData.replace("</head", baseTag + "</head");
        next();
      };
    },

    /**
     * Publish a page to S3. If it's a publish by
     * the owning user, this effects an update. Otherwise,
     * this will create a new S3 object (=page).
     */
    publishData: function(options) {
      // NOTE: workaround until https://github.com/LearnBoost/knox/issues/194 is addressed.
      //       this line prevents knox from forming url-validation-failing S3 URLs.
      if(!options.port) { delete options.port; }

      var knox = require("knox"),
          s3 = knox.createClient(options);

      return function(req, res, next) {
        var userId = req.session.username,
            data = req.body.finalizedHTML,
            headers = {
              'x-amz-acl': 'public-read',
              'Content-Length': Buffer.byteLength(data,'utf8'),
              'Content-Type': 'text/html;charset=UTF-8'
            };

        // TODO: proper mapping for old->new ids.
        // See: https://bugzilla.mozilla.org/show_bug.cgi?id=862911
        var location = req.publishLocation,
            // FIXME: Plan for S3 being down. This is not the ideal error handling,
            //        but is a working stub in lieu of a proper solution.
            // See: https://bugzilla.mozilla.org/show_bug.cgi?id=865738
            s3PublishError = new Error("There was a problem publishing the page. Your page has been saved"+
                                       " with id "+req.publishId+", so you can edit it, but it could not be"+
                                       " published to the web."),
            s3Error = new Error("failure during publish step (error "+res.statusCode+")");

        // write data to S3
        s3.put(location + "_", headers)
          .on("error", function(err) {
            next(s3PublishError);
          })
          .on("response", function(res) {
            if (res.statusCode === 200) {
              var embedShell = req.body.embedShell;
              headers = {
                'x-amz-acl': 'public-read',
                'Content-Length': Buffer.byteLength(embedShell,'utf8'),
                'Content-Type': 'text/html;charset=UTF-8'
              };
              s3.put(location, headers)
                .on("error", function(err) {
                  next(s3PublishError);
                })
                .on("response", function(res) {
                  if (res.statusCode === 200) {
                    req.publishedUrl = s3.url(location);
                    metrics.increment('project.publish.success');

                    // publish .../edit and .../remix redirecting routes to S3 as well.
                    var target = req.remixUrl.replace(/\/remix$/,'');
                    ["edit","remix"].forEach(function(suffix) {
                      var thimbletarget = target + '/'+ suffix;
                      var s3target = location + '/' + suffix;

                      // This currently uses meta-refresh, but should be switched over to
                      // native S3 redirect headers instead, once we figure out how.
                      // SEE: https://bugzilla.mozilla.org/show_bug.cgi?id=876763
                      var redirect = "<!doctype html><meta http-equiv='refresh' content='0; url="+thimbletarget+"'>";

                      headers = {
                        'x-amz-acl': 'public-read',
                        'Content-Length': Buffer.byteLength(redirect,'utf8'),
                        'Content-Type': 'text/html;charset=UTF-8'
                      };

                      // NOTE: failure to set up the /edit and /remix links
                      //       should not actually break publishing, so we
                      //       don't catch on(error) or on(response).
                      s3.put(s3target, headers).end(redirect);
                    });
                    next();
                  } else {
                    metrics.increment('project.publish.error');
                    next(s3Error);
                  }
                })
                .end(embedShell);
            } else {
              metrics.increment('project.publish.error');
              next(s3Error);
            }
          })
          .end(data);
      };
    },
    /**
     * Turn the S3 URL into a user subdomain
     */
    rewriteUrl: function(req, res, next) {
      if (req.customUrl) {
        req.publishedUrl = req.customUrl;
      }
      next();
    },

    /**
     * Publish a page to the makeAPI. If it's "our" page,
     * update, otherwise, create.
     */
    publishMake: function(make) {
      return function(req, res, next) {
        var metaData = req.body.metaData,
            options = {
              thumbnail: metaData.thumbnail,
              contentType: "application/x-thimble",
              title: metaData.title || "",
              description: metaData.description || "",
              author: metaData.author || "",
              locale: metaData.locale || "",
              email: req.session.email,
              url: req.publishedUrl,
              contenturl: req.publishedUrl + "_",
              remixedFrom: req.body.remixedFrom,
              remixUrl: req.remixUrl,
              tags: metaData.tags ? metaData.tags.split(",") : []
            };

        function finalizePublishMake(err, result) {
          if (err) {
            metrics.increment('makeapi.publish.error');
            next(err);
          } else {
            metrics.increment('makeapi.publish.success');
            next();
          }
        }

        // Publish the make to the makeapi.
        // If it exists and we own it, update it.
        // Otherwise create a new one.
        make.search({
          email: req.session.email,
          url: req.oldUrl || req.publishedUrl
        }, function(err, results) {
          if (err) {
            return finalizePublishMake(err);
          }

          var result = results[0];
          if (result) {
            make.update(result.id, options, finalizePublishMake);
          } else {
            make.create(options, finalizePublishMake);
          }
        });
      };
    },

    /**
     * Unpublish (delete/remove) a project.
     */
    deleteProject: function(databaseAPI) {
      return function(req, res, next) {
        databaseAPI.destroy(req.requestId, function(err, project) {
          if(err) {
            next(err);
          }
          res.json({"status": 200});
        });
      };
    }
  };
};
<|endoftext|>


<commit_before>var _ = require('underscore');
var path = require('path');
var request = require('request');
var source = require('../lib/source');
var style = require('../lib/style');
var task = require('../lib/task');
var tm = require('../lib/tm');

// Load defaults for new styles
var defaults = {};
style.info('tmstyle://' + path.dirname(require.resolve('tm2-default-style')), function(err, info) {
    if (err) throw err;
    var data = JSON.parse(JSON.stringify(info));
    delete data.id;
    defaults.style = data;
});

var middleware = {};

// Load user tilesets from the Mapbox API and include in source history.
middleware.userTilesets = function(req, res, next) {
    var oauth = tm.db.get('oauth');
    if (!oauth) return next(new Error('oauth required'));
    req.accesstoken = oauth.accesstoken;
    var url = tm.config().mapboxauth+'/api/Map?account='+oauth.account+'&_type=tileset&private=true&access_token='+oauth.accesstoken;
    request(url, function(err, resp, body) {
        if (err) return next(err);
        if (resp.statusCode !== 200) return next(new Error(resp.statusCode + ' GET ' + url));
        var tilesets;
        try { tilesets = JSON.parse(body); } catch(err) { next(err); }
        tilesets.forEach(function(t) {
            if (!t.metadata) return;
            if (t.metadata.format !== 'pbf') return;
            tm.history('mapbox:///' + t.id);
        });
        next();
    });
};

middleware.history = function(req, res, next) {
    req.history = { style:[], source:[] };
    var queue = tm.history().slice(0);
    load();

    function load() {
        if (!queue.length) return next();
        var id = queue.shift();
        var type = id.indexOf('tmstyle://') === 0 ? 'style' : 'source';
        var method = type === 'style' ? style.info : source.info;
        method(id, function(err, info) {
            if (err) {
                tm.history(id, true);
            } else {
                req.history[type] = req.history[type] || {};
                req.history[type][id] = info;
            }
            load();
        });
    }
};

middleware.writeStyle = function(req, res, next) {
    var data = req.body && req.body.id ? req.body : _({id:style.tmpid()}).defaults(defaults.style);

    var write = function(err) {
        if (err) return next(err);
        style.save(data, function(err, s) {
            if (err) return next(err);
            if (!style.tmpid(s.data.id)) tm.history(s.data.id);
            req.style = s;
            next();
        });
    }
    if (data._recache && req.query.id) return source.invalidate(req.body.source, write);

    // Let source for new styles be specified via querystring.
    if (!req.body.id && req.query && req.query.source) return source(req.query.source, function(err, s) {
        if (err) return next(err);
        data.styles = {
            'style.mss': tm.templates.xraydefaultcarto(s.data.vector_layers)
        };
        data.source = req.query.source;
        write();
    });

    write();
}

middleware.loadStyle = function(req, res, next) {
    style(req.query.id, function(err, s) {
        if (err && err.code === 'ENOENT' && style.tmpid(req.query.id)) return middleware.writeStyle(req, res, next);
        if (err) return next(err);
        if (!style.tmpid(s.data.id)) tm.history(s.data.id);
        req.style = s;
        next();
    });
};

middleware.writeSource = function(req, res, next) {
    var data = req.body && req.body.id ? req.body : {id: source.tmpid()};
    source.save(data, function(err, s) {
        if (err) return next(err);
        if (!source.tmpid(s.data.id)) tm.history(s.data.id);
        req.source = s;
        next();
    });
};

middleware.loadSource = function(req, res, next) {
    source(req.query.id, function(err, s) {
        if (err && err.code === 'ENOENT' && source.tmpid(req.query.id)) return middleware.writeSource(req, res, next);
        if (err) return next(err);
        if (!source.tmpid(s.data.id)) tm.history(s.data.id);
        req.source = s;
        req.style = s.style;
        next();
    });
};

middleware.auth = function(req, res, next) {
    if (!tm.db.get('oauth')) {
        var err = new Error('No active OAuth account');
        err.code = 'EOAUTH';
        return next(err);
    }
    next();
};

// Check for an active export. If present, redirect to the export page
// effectively locking the application from use until export is complete.
middleware.exporting = function(req, res, next) {
    var activeTask = task.get();
    if (activeTask && activeTask.type === 'export' && (req.path !== '/mbtiles' || req.query.id !== activeTask.id)) {
        res.redirect('/mbtiles?id=' + activeTask.id);
    }else if (activeTask && activeTask.type === 'upload' && (req.path !== '/upload' || req.query.id !== activeTask.id)) {
        res.redirect('/upload?id=' + activeTask.id);
    } else {
        next();
    }
};

module.exports = middleware;
<commit_msg>style/source are objects, not arrays.<commit_after>var _ = require('underscore');
var path = require('path');
var request = require('request');
var source = require('../lib/source');
var style = require('../lib/style');
var task = require('../lib/task');
var tm = require('../lib/tm');

// Load defaults for new styles
var defaults = {};
style.info('tmstyle://' + path.dirname(require.resolve('tm2-default-style')), function(err, info) {
    if (err) throw err;
    var data = JSON.parse(JSON.stringify(info));
    delete data.id;
    defaults.style = data;
});

var middleware = {};

// Load user tilesets from the Mapbox API and include in source history.
middleware.userTilesets = function(req, res, next) {
    var oauth = tm.db.get('oauth');
    if (!oauth) return next(new Error('oauth required'));
    req.accesstoken = oauth.accesstoken;
    var url = tm.config().mapboxauth+'/api/Map?account='+oauth.account+'&_type=tileset&private=true&access_token='+oauth.accesstoken;
    request(url, function(err, resp, body) {
        if (err) return next(err);
        if (resp.statusCode !== 200) return next(new Error(resp.statusCode + ' GET ' + url));
        var tilesets;
        try { tilesets = JSON.parse(body); } catch(err) { next(err); }
        tilesets.forEach(function(t) {
            if (!t.metadata) return;
            if (t.metadata.format !== 'pbf') return;
            tm.history('mapbox:///' + t.id);
        });
        next();
    });
};

middleware.history = function(req, res, next) {
    req.history = { style:{}, source:{} };
    var queue = tm.history().slice(0);
    load();

    function load() {
        if (!queue.length) return next();
        var id = queue.shift();
        var type = id.indexOf('tmstyle://') === 0 ? 'style' : 'source';
        var method = type === 'style' ? style.info : source.info;
        method(id, function(err, info) {
            if (err) {
                tm.history(id, true);
            } else {
                req.history[type] = req.history[type] || {};
                req.history[type][id] = info;
            }
            load();
        });
    }
};

middleware.writeStyle = function(req, res, next) {
    var data = req.body && req.body.id ? req.body : _({id:style.tmpid()}).defaults(defaults.style);

    var write = function(err) {
        if (err) return next(err);
        style.save(data, function(err, s) {
            if (err) return next(err);
            if (!style.tmpid(s.data.id)) tm.history(s.data.id);
            req.style = s;
            next();
        });
    }
    if (data._recache && req.query.id) return source.invalidate(req.body.source, write);

    // Let source for new styles be specified via querystring.
    if (!req.body.id && req.query && req.query.source) return source(req.query.source, function(err, s) {
        if (err) return next(err);
        data.styles = {
            'style.mss': tm.templates.xraydefaultcarto(s.data.vector_layers)
        };
        data.source = req.query.source;
        write();
    });

    write();
}

middleware.loadStyle = function(req, res, next) {
    style(req.query.id, function(err, s) {
        if (err && err.code === 'ENOENT' && style.tmpid(req.query.id)) return middleware.writeStyle(req, res, next);
        if (err) return next(err);
        if (!style.tmpid(s.data.id)) tm.history(s.data.id);
        req.style = s;
        next();
    });
};

middleware.writeSource = function(req, res, next) {
    var data = req.body && req.body.id ? req.body : {id: source.tmpid()};
    source.save(data, function(err, s) {
        if (err) return next(err);
        if (!source.tmpid(s.data.id)) tm.history(s.data.id);
        req.source = s;
        next();
    });
};

middleware.loadSource = function(req, res, next) {
    source(req.query.id, function(err, s) {
        if (err && err.code === 'ENOENT' && source.tmpid(req.query.id)) return middleware.writeSource(req, res, next);
        if (err) return next(err);
        if (!source.tmpid(s.data.id)) tm.history(s.data.id);
        req.source = s;
        req.style = s.style;
        next();
    });
};

middleware.auth = function(req, res, next) {
    if (!tm.db.get('oauth')) {
        var err = new Error('No active OAuth account');
        err.code = 'EOAUTH';
        return next(err);
    }
    next();
};

// Check for an active export. If present, redirect to the export page
// effectively locking the application from use until export is complete.
middleware.exporting = function(req, res, next) {
    var activeTask = task.get();
    if (activeTask && activeTask.type === 'export' && (req.path !== '/mbtiles' || req.query.id !== activeTask.id)) {
        res.redirect('/mbtiles?id=' + activeTask.id);
    }else if (activeTask && activeTask.type === 'upload' && (req.path !== '/upload' || req.query.id !== activeTask.id)) {
        res.redirect('/upload?id=' + activeTask.id);
    } else {
        next();
    }
};

module.exports = middleware;
<|endoftext|>


<commit_before>/*jshint node:true */

"use strict";

var blacklist = require('./blacklist'),
    mime      = require('mime'),
    path      = require('path');

var request = require('request').defaults({
    followRedirect: false,
    jar           : false,
    strictSSL     : true,
    timeout       : 30000
});

// Public directory containing static files.
var publicDir = require('fs').realpathSync(__dirname + '/../public');

// Whitelist of file extensions that will be proxied through rawgithub.com. All
// others will be redirected to raw.github.com.
var extensionWhitelist = {
     '.css' : true,
     '.htm' : true,
     '.html': true,
     '.js'  : true,
     '.rss' : true,
     '.svg' : true,
     '.xml' : true
};

// Array of request header names that should be relayed from the user to GitHub.
var relayRequestHeaders = [
    'Accept',
    'Accept-Charset',
    'If-Modified-Since',
    'If-None-Match',
    'User-Agent'
];

// Array of request header names that should be relayed from GitHub to the user.
var relayResponseHeaders = [
    'Date',
    'ETag'
];

module.exports = {

    // Checks the blacklist to see if this request should be denied.
    blacklist: function (req, res, next) {
        if (!blacklist.referrers.length && !blacklist.uris.length) {
            next();
            return;
        }

        var i, len, uri;

        // Check the URI blacklist. Blacklisted URIs are served a 403 Forbidden
        // response, but won't receive evil.js or evil.css.
        for (i = 0, len = blacklist.uris.length; i < len; i++) {
            uri = blacklist.uris[i];

            if (typeof uri === 'string') {
                if (uri === req.path) {
                    res.set('X-Blacklist', 'uri');
                    module.exports.deny(req, res);
                    return;
                }
            } else {
                if (uri.test(req.path)) {
                    res.set('X-Blacklist', 'uri');
                    module.exports.deny(req, res);
                    return;
                }
            }
        }

        var referrer = req.get('referrer'),
            badReferrer;

        if (!referrer) {
            next();
            return;
        }

        // Check the referrer blacklist. Requests from blacklisted referrers
        // will receive evil.js or evil.css.
        for (i = 0, len = blacklist.referrers.length; i < len; i++) {
            badReferrer = blacklist.referrers[i];

            if (typeof badReferrer === 'string') {
                if (badReferrer === referrer) {
                    res.set('X-Blacklist', 'referrer');
                    module.exports.evil(req, res);
                    return;
                }
            } else {
                if (badReferrer.test(referrer)) {
                    res.set('X-Blacklist', 'referrer');
                    module.exports.evil(req, res);
                    return;
                }
            }
        }

        next();
    },

    // Sends a 403 response for blacklisted URIs.
    deny: function (req, res) {
        res.status(403);
        res.type('txt').send('The requested URI has been blacklisted due to abuse.');
    },

    // Sends a generic 403 Forbidden response.
    error403: function (req, res) {
        res.status(403);

        if (req.accepts('html')) {
            res.sendfile(publicDir + '/errors/403.html');
            return;
        }

        res.type('txt').send('Not cool, man.');
    },

    // Serves up evil.js or evil.css, or just a 403, depending on the requested
    // filename.
    evil: function (req, res) {
        var extname = path.extname(req.path.toLowerCase());

        if (extname === '.js') {
            res.sendfile(publicDir + '/js/evil.js', {
                maxAge: 86400000 // 1 day
            });
        } else if (extname === '.css') {
            res.sendfile(publicDir + '/css/evil.css', {
                maxAge: 86400000 // 1 day
            });
        } else {
            res.status(403);
            res.type('txt').send('The referring website has been blacklisted for abusing rawgithub.com.');
        }
    },

    // Redirects requests for non-whitelisted file extensions directly to
    // GitHub, since there's no value in proxying them.
    fileRedirect: function (rootUrl) {
        return function (req, res, next) {
            if (extensionWhitelist[path.extname(req.path).toLowerCase()]) {
                next();
            } else {
                res.set('Cache-Control', 'max-age=157680000'); // 5 years
                res.redirect(301, rootUrl + req.url);
            }
        };
    },

    // Sets the X-Robots-Tag response header to disallow indexing and following.
    noRobots: function (req, res, next) {
        res.set('X-Robots-Tag', 'none');
        next();
    },

    // Returns a middleware function that proxies a request to the specified
    // rootUrl with the current request URL appended, passes through
    // appropriate request and response headers, and relays the body of the
    // response to the client, ending the request.
    proxyPath: function (rootUrl) {
        return function (req, res, next) {
            var headers = {};

            relayRequestHeaders.forEach(function (header) {
                var value = req.header(header);

                if (value) {
                    headers[header] = value;
                }
            });

            request(rootUrl + req.path, {
                headers: headers
            }).on('response', function (gh) {
                var status = gh.statusCode;

                if ((status < 200 || status > 299) && status !== 304) {
                    // Treat as a 404.
                    next();
                    return;
                }

                // Pass certain GitHub headers along in the response.
                relayResponseHeaders.forEach(function (name) {
                    var value = gh.headers[name.toLowerCase()];

                    if (value) {
                        res.set(name, value);
                    }
                });

                var type = mime.lookup(req.path);

                // If GitHub served this file with a specific character
                // encoding, so should we.
                var charset = /;\s*charset\s*=\s*([^\s;]+)/.exec(gh.headers['content-type']);

                if (charset) {
                    type += '; charset=' + charset[1];
                }

                res.statusCode = status;
                res.type(type);

                gh.pipe(res);
            }).on('error', function (err) {
                next(err);
            });
        };
    },

    // Gather stats on an incoming request.
    stats: require('./stats').middleware

};
<commit_msg>Allow SWFs.<commit_after>/*jshint node:true */

"use strict";

var blacklist = require('./blacklist'),
    mime      = require('mime'),
    path      = require('path');

var request = require('request').defaults({
    followRedirect: false,
    jar           : false,
    strictSSL     : true,
    timeout       : 30000
});

// Public directory containing static files.
var publicDir = require('fs').realpathSync(__dirname + '/../public');

// Whitelist of file extensions that will be proxied through rawgithub.com. All
// others will be redirected to raw.github.com.
var extensionWhitelist = {
     '.css' : true,
     '.htm' : true,
     '.html': true,
     '.js'  : true,
     '.rss' : true,
     '.svg' : true,
     '.swf' : true,
     '.xml' : true
};

// Array of request header names that should be relayed from the user to GitHub.
var relayRequestHeaders = [
    'Accept',
    'Accept-Charset',
    'If-Modified-Since',
    'If-None-Match',
    'User-Agent'
];

// Array of request header names that should be relayed from GitHub to the user.
var relayResponseHeaders = [
    'Date',
    'ETag'
];

module.exports = {

    // Checks the blacklist to see if this request should be denied.
    blacklist: function (req, res, next) {
        if (!blacklist.referrers.length && !blacklist.uris.length) {
            next();
            return;
        }

        var i, len, uri;

        // Check the URI blacklist. Blacklisted URIs are served a 403 Forbidden
        // response, but won't receive evil.js or evil.css.
        for (i = 0, len = blacklist.uris.length; i < len; i++) {
            uri = blacklist.uris[i];

            if (typeof uri === 'string') {
                if (uri === req.path) {
                    res.set('X-Blacklist', 'uri');
                    module.exports.deny(req, res);
                    return;
                }
            } else {
                if (uri.test(req.path)) {
                    res.set('X-Blacklist', 'uri');
                    module.exports.deny(req, res);
                    return;
                }
            }
        }

        var referrer = req.get('referrer'),
            badReferrer;

        if (!referrer) {
            next();
            return;
        }

        // Check the referrer blacklist. Requests from blacklisted referrers
        // will receive evil.js or evil.css.
        for (i = 0, len = blacklist.referrers.length; i < len; i++) {
            badReferrer = blacklist.referrers[i];

            if (typeof badReferrer === 'string') {
                if (badReferrer === referrer) {
                    res.set('X-Blacklist', 'referrer');
                    module.exports.evil(req, res);
                    return;
                }
            } else {
                if (badReferrer.test(referrer)) {
                    res.set('X-Blacklist', 'referrer');
                    module.exports.evil(req, res);
                    return;
                }
            }
        }

        next();
    },

    // Sends a 403 response for blacklisted URIs.
    deny: function (req, res) {
        res.status(403);
        res.type('txt').send('The requested URI has been blacklisted due to abuse.');
    },

    // Sends a generic 403 Forbidden response.
    error403: function (req, res) {
        res.status(403);

        if (req.accepts('html')) {
            res.sendfile(publicDir + '/errors/403.html');
            return;
        }

        res.type('txt').send('Not cool, man.');
    },

    // Serves up evil.js or evil.css, or just a 403, depending on the requested
    // filename.
    evil: function (req, res) {
        var extname = path.extname(req.path.toLowerCase());

        if (extname === '.js') {
            res.sendfile(publicDir + '/js/evil.js', {
                maxAge: 86400000 // 1 day
            });
        } else if (extname === '.css') {
            res.sendfile(publicDir + '/css/evil.css', {
                maxAge: 86400000 // 1 day
            });
        } else {
            res.status(403);
            res.type('txt').send('The referring website has been blacklisted for abusing rawgithub.com.');
        }
    },

    // Redirects requests for non-whitelisted file extensions directly to
    // GitHub, since there's no value in proxying them.
    fileRedirect: function (rootUrl) {
        return function (req, res, next) {
            if (extensionWhitelist[path.extname(req.path).toLowerCase()]) {
                next();
            } else {
                res.set('Cache-Control', 'max-age=157680000'); // 5 years
                res.redirect(301, rootUrl + req.url);
            }
        };
    },

    // Sets the X-Robots-Tag response header to disallow indexing and following.
    noRobots: function (req, res, next) {
        res.set('X-Robots-Tag', 'none');
        next();
    },

    // Returns a middleware function that proxies a request to the specified
    // rootUrl with the current request URL appended, passes through
    // appropriate request and response headers, and relays the body of the
    // response to the client, ending the request.
    proxyPath: function (rootUrl) {
        return function (req, res, next) {
            var headers = {};

            relayRequestHeaders.forEach(function (header) {
                var value = req.header(header);

                if (value) {
                    headers[header] = value;
                }
            });

            request(rootUrl + req.path, {
                headers: headers
            }).on('response', function (gh) {
                var status = gh.statusCode;

                if ((status < 200 || status > 299) && status !== 304) {
                    // Treat as a 404.
                    next();
                    return;
                }

                // Pass certain GitHub headers along in the response.
                relayResponseHeaders.forEach(function (name) {
                    var value = gh.headers[name.toLowerCase()];

                    if (value) {
                        res.set(name, value);
                    }
                });

                var type = mime.lookup(req.path);

                // If GitHub served this file with a specific character
                // encoding, so should we.
                var charset = /;\s*charset\s*=\s*([^\s;]+)/.exec(gh.headers['content-type']);

                if (charset) {
                    type += '; charset=' + charset[1];
                }

                res.statusCode = status;
                res.type(type);

                gh.pipe(res);
            }).on('error', function (err) {
                next(err);
            });
        };
    },

    // Gather stats on an incoming request.
    stats: require('./stats').middleware

};
<|endoftext|>


<commit_before>'use strict';

var bodyParser = require('body-parser'),
    cookieParser = require('cookie-parser'),
    session = require('express-session'),
    flash = require('express-flash'),
    multer  = require('multer'),
    helmet = require('helmet'),
    compression = require('compression'),
    expressLogger = require('./express-logger');

var RedisStore = require('connect-redis')(session);
var timeout = require('connect-timeout');

var middlewareOptions = require('./middlewareOptions');
// var logger = require('./logger');

module.exports = function(done, app, options) {
    app.use(expressLogger.logger);
    app.use(expressLogger.errorLogger);

    if (options.csp !== false) {
        app.use(helmet.contentSecurityPolicy(middlewareOptions('csp', options)));
    }

    app.use(helmet.xssFilter());
    app.use(helmet.frameguard.apply(this, middlewareOptions('frameguard', options)));
    app.use(helmet.hsts(middlewareOptions('hsts', options)));
    // app.use(helmet.crossdomain(middlewareOptions('crossdomain', options)));
    app.use(helmet.noSniff());
    app.use(helmet.ieNoOpen());

    app.use(cookieParser());

    var _sessionOptions = middlewareOptions('session', options);
    _sessionOptions.store = _sessionOptions.store;

    if (!_sessionOptions.store && options.redisClient) {
        _sessionOptions.store = new RedisStore({client: options.redisClient, ttl: 60 * 60});
    }

    app.use(session(_sessionOptions));

    var _bodyParserOptions = middlewareOptions('bodyParser', options);
    app.use(bodyParser.json(_bodyParserOptions.json));
    app.use(bodyParser.urlencoded(_bodyParserOptions.urlencoded));

    app.use(flash());
    app.use(multer(middlewareOptions('multer', options)));
    app.use(compression(middlewareOptions('compression', options)));
    app.use(timeout(middlewareOptions('timeout', options).ms));

    app.disable('x-powered-by');

    done();
};<commit_msg>Allow X-Frame-Option to not be sent<commit_after>'use strict';

var bodyParser = require('body-parser'),
    cookieParser = require('cookie-parser'),
    session = require('express-session'),
    flash = require('express-flash'),
    multer  = require('multer'),
    helmet = require('helmet'),
    compression = require('compression'),
    expressLogger = require('./express-logger');

var RedisStore = require('connect-redis')(session);
var timeout = require('connect-timeout');

var middlewareOptions = require('./middlewareOptions');
// var logger = require('./logger');

module.exports = function(done, app, options) {
    app.use(expressLogger.logger);
    app.use(expressLogger.errorLogger);

    if (options.csp !== false) {
        app.use(helmet.contentSecurityPolicy(middlewareOptions('csp', options)));
    }
    if(options.frameguard !== false){
        app.use(helmet.frameguard.apply(this, middlewareOptions('frameguard', options)));
    }
    app.use(helmet.xssFilter());
    app.use(helmet.hsts(middlewareOptions('hsts', options)));
    // app.use(helmet.crossdomain(middlewareOptions('crossdomain', options)));
    app.use(helmet.noSniff());
    app.use(helmet.ieNoOpen());

    app.use(cookieParser());

    var _sessionOptions = middlewareOptions('session', options);
    _sessionOptions.store = _sessionOptions.store;

    if (!_sessionOptions.store && options.redisClient) {
        _sessionOptions.store = new RedisStore({client: options.redisClient, ttl: 60 * 60});
    }

    app.use(session(_sessionOptions));

    var _bodyParserOptions = middlewareOptions('bodyParser', options);
    app.use(bodyParser.json(_bodyParserOptions.json));
    app.use(bodyParser.urlencoded(_bodyParserOptions.urlencoded));

    app.use(flash());
    app.use(multer(middlewareOptions('multer', options)));
    app.use(compression(middlewareOptions('compression', options)));
    app.use(timeout(middlewareOptions('timeout', options).ms));

    app.disable('x-powered-by');

    done();
};
<|endoftext|>


<commit_before>// node modules
const config = require('./config');
const db = require('./db');
const User = require('./models/user');

exports.authStatus = (req, res, next) => {

  if (!req.headers.authorization) {
    req.authStatus = 'missing';
  } else {
    const token = req.headers.authorization.replace(/(B|b)earer /, '');
    req.authStatus = res.locals.user.checkToken(token);
  }

  next();

};

exports.error404 = (req, res) => {
  r.res(null, 404, res);
};

exports.error500 = (err, req, res) => {
  console.error(err);
  r.res(err, null, res);
};

exports.hbsOptions = {
  defaultLayout: 'main',

  helpers: {
    // don't use arrow functions here - need to retain value of `this`
    section: function (name, options) {
      if (!this.sections) { this.sections = {}; }
      this.sections[name] = options.fn(this);
      return null;
    },

    baseUrl: path => {
      return config.mapBaseUrl(path);
    }
  }
};

exports.logUrl = (req, res, next) => {
  console.log('Requested URL:', req.url);
  next();
};

exports.manageBody = (req, res, next) => {
  if (req.method === 'PUT' && req.headers['content-type'] !== 'application/json') {
    req.headers['content-type'] = 'application/json';
  }
  next();
};

exports.manageLogin = (req, res, next) => {

  const resetCookie = userRid => {
    res.cookie('user', userRid, { maxAge: 14400000, signed: true });
  };

  res.logout = () => {
    res.clearCookie('user');
    res.locals.user = {};
  };

  res.login = rid => resetCookie(rid);

  if (req.query.logout) {
    res.logout();
    req.loggedIn = false;
    res.redirect('/');
  } else if (req.signedCookies.user || req.headers.authorization) {
    console.log('decrypt:', decrypt);
    console.log('cookie:', req.signedCookies.user);
    console.log('auth header:', req.headers.authorization);
    console.log('token', req.headers.authorization.replace(/(B|b)earer /, ''));
    console.log(decrypt(req.headers.authorization.replace(/(B|b)earer /, '')));
    db.getUser(req.signedCookies.user || decrypt(req.headers.authorization.replace(/(B|b)earer /, '')))
    .then(user => {
      console.log('User response:', user);
      resetCookie(req.signedCookies.user);
      res.locals.user = new User(user);
      req.loggedIn = true;
      next();
    })
    .catch(err => r.res(err, null, res));
  } else {
    req.loggedIn = false;
    next();
  }

};

exports.manageQueries = (req, res, next) => {
  if (req.query.ids) { req.query.ids = req.query.ids.split(','); }
  next();
};
<commit_msg>potential fix<commit_after>// node modules
const config = require('./config');
const db = require('./db');
const User = require('./models/user');

exports.authStatus = (req, res, next) => {

  if (!req.headers.authorization) {
    req.authStatus = 'missing';
  } else {
    const token = req.headers.authorization.replace(/(B|b)earer /, '');
    req.authStatus = res.locals.user.checkToken(token);
  }

  next();

};

exports.error404 = (req, res) => {
  r.res(null, 404, res);
};

exports.error500 = (err, req, res) => {
  console.error(err);
  r.res(err, null, res);
};

exports.hbsOptions = {
  defaultLayout: 'main',

  helpers: {
    // don't use arrow functions here - need to retain value of `this`
    section: function (name, options) {
      if (!this.sections) { this.sections = {}; }
      this.sections[name] = options.fn(this);
      return null;
    },

    baseUrl: path => {
      return config.mapBaseUrl(path);
    }
  }
};

exports.logUrl = (req, res, next) => {
  console.log('Requested URL:', req.url);
  next();
};

exports.manageBody = (req, res, next) => {
  if (req.method === 'PUT' && req.headers['content-type'] !== 'application/json') {
    req.headers['content-type'] = 'application/json';
  }
  next();
};

exports.manageLogin = (req, res, next) => {

  const resetCookie = userRid => {
    res.cookie('user', userRid, { maxAge: 14400000, signed: true });
  };

  res.logout = () => {
    res.clearCookie('user');
    res.locals.user = {};
  };

  res.login = rid => resetCookie(rid);

  if (req.query.logout) {
    res.logout();
    req.loggedIn = false;
    res.redirect('/');
  } else if (req.signedCookies.user || req.headers.authorization) {
    console.log('pre-rid');
    const rid = req.headers.authorization ? decrypt(req.headers.authorization.replace(/(B|b)earer /, '')) : req.signedCookies.user;
    console.log('rid:', rid);
    db.getUser(rid)
    .then(user => {
      console.log('User response:', user);
      resetCookie(req.signedCookies.user);
      res.locals.user = new User(user);
      req.loggedIn = true;
      next();
    })
    .catch(err => r.res(err, null, res));
  } else {
    req.loggedIn = false;
    next();
  }

};

exports.manageQueries = (req, res, next) => {
  if (req.query.ids) { req.query.ids = req.query.ids.split(','); }
  next();
};
<|endoftext|>


<commit_before>// Simple shortcut...
export const $body = document.body;

// DOM selectors. The results are returned in arrays.
// http://lea.verou.me/2015/04/jquery-considered-harmful/
export function $(selectors, container) {
  return (typeof selectors === "string") ?
          (container || document).querySelector(selectors) :
          null;
}

export function $$(selectors, container) {
  return [].slice.call((container || document).querySelectorAll(selectors));
}

// Checks to see if an element is in the page"s body.
// https://developer.mozilla.org/en-US/docs/Web/API/Node/contains
export function isInPage(node) {
  return (node === $body) ? false : $body.contains(node);
}

// Assigns a css class to a html element.
// This function is mainly used for maintain the compatibility with IE9.
export function addClass(element, cname) {
  if ("classList" in element) {
    element.classList.add(cname);
  } else { // IE9
    element.className.baseVal += " " + cname;
  }
}

// Helper for remove a css class
// http://stackoverflow.com/questions/2155737/remove-css-class-from-element-with-javascript-no-jquery#2155787
// export function delClass(element, cname) {
//     if ("classList" in element) {
//         element.classList.remove(cname);
//     } else {
//         element.className.baseVal.replace(
//             new RegExp("(?:^|\\s)" + cname + "(?!\\S)"),
//             ""
//         );
//     }
// }

// Removes an element from a DOM branch
export function removeElement(element) {
  return element.parentNode.removeChild(element);
}

// Returns an array of a given object's own enumerable property values
// https://stackoverflow.com/questions/6857468/a-better-way-to-convert-js-object-to-array/26166303#26166303
export function objValues(obj) {
  if (Object.values) {
    return Object.values(obj);
  } else {
    return Object.keys(obj).map(key => obj[key]);
  }
}

// Generates a DOM id with customized basename.
// The generator uses a counter addeds to the end of the basename for ensure
// uniqueness of the id
// https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute
export const generateID = (function() {
  let counter = 0;
  const fn = function generateID(prefix) {
    if (!prefix) {
      prefix = "u_";
    }
    ++counter;
    return prefix + counter.toString(36);
  };
  return fn;
})();
<commit_msg>Add a creator SVG element helper<commit_after>// Simple shortcut...
export const $body = document.body;

// DOM selectors. The results are returned in arrays.
// http://lea.verou.me/2015/04/jquery-considered-harmful/
export function $(selectors, container) {
  return (typeof selectors === "string") ?
          (container || document).querySelector(selectors) :
          null;
}

export function $$(selectors, container) {
  return [].slice.call((container || document).querySelectorAll(selectors));
}

// Checks to see if an element is in the page"s body.
// https://developer.mozilla.org/en-US/docs/Web/API/Node/contains
export function isInPage(node) {
  return (node === $body) ? false : $body.contains(node);
}

// Assigns a css class to a html element.
// This function is mainly used for maintain the compatibility with IE9.
export function addClass(element, cname) {
  if ("classList" in element) {
    element.classList.add(cname);
  } else { // IE9
    element.className.baseVal += " " + cname;
  }
}

// Helper for remove a css class
// http://stackoverflow.com/questions/2155737/remove-css-class-from-element-with-javascript-no-jquery#2155787
// export function delClass(element, cname) {
//     if ("classList" in element) {
//         element.classList.remove(cname);
//     } else {
//         element.className.baseVal.replace(
//             new RegExp("(?:^|\\s)" + cname + "(?!\\S)"),
//             ""
//         );
//     }
// }

// Creates a new SVG Element (Only a shortcut).
export function createSVGElement(tagName) {
  return document.createElementNS(ns.svg, tagName);
}

// Removes an element from a DOM branch.
export function removeElement(element) {
  return element.parentNode.removeChild(element);
}

// Returns an array of a given object's own enumerable property values
// https://stackoverflow.com/questions/6857468/a-better-way-to-convert-js-object-to-array/26166303#26166303
export function objValues(obj) {
  if (Object.values) {
    return Object.values(obj);
  } else {
    return Object.keys(obj).map(key => obj[key]);
  }
}

// Generates a DOM id with customized basename.
// The generator uses a counter addeds to the end of the basename for ensure
// uniqueness of the id
// https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute
export const generateID = (function() {
  let counter = 0;
  const fn = function generateID(prefix) {
    if (!prefix) {
      prefix = "u_";
    }
    ++counter;
    return prefix + counter.toString(36);
  };
  return fn;
})();
<|endoftext|>


<commit_before>'use strict';

var MongoClient = require('mongodb').MongoClient
var cheerio = require('cheerio');
var Q = require('q');
var _ = require('lodash');

var mongoUri = process.env.MONGOLAB_URI ||
    process.env.MONGOHQ_URL ||
    'mongodb://localhost/prerender';

var db = Q.denodeify(MongoClient.connect)(mongoUri);
var table = function(domain){
    return db.then(function (db) {
        return Q.denodeify(db.collection.bind(db))(domain);
    });
};

module.exports = {
    beforePhantomRequest: function(req, res, next) {
        var uri = uriFromPath(req.url);
        if (req.method === 'DELETE') {
            del(uri).then(function () {
                res.send(200, uri + ' deleted.');
            }).catch(reportAndProceed(next));
        } else if (req.method === 'PUT' || req.method === 'POST') {
            // Just pass through so that Phantom will render the page
            next();
        } else if (req.method === 'GET') {
            get(uri).then(function (document) {
                console.log('Serving from cache:', uri);

                _.each(document.headers, function (value, key) {
                    res.setHeader(key, value);
                });

                res.send(document.statusCode, document.content);
            }).catch(reportAndProceed(next));
        } else {
            res.send(400, 'Only GET, POST, PUT and DELETE methods are supported.');
        }
    },

    afterPhantomRequest: function(req, res, next) {
        var uri = uriFromPath(req.url);

        var original = originalHead(req);
        var overrides = headOverrides(req);
        var head = _.merge({}, original, overrides);

        var document = {
            content: req.prerender.documentHTML,
            statusCode: head.statusCode,
            headers: head.headers,
            originalHead: original,
            overrides: overrides
        };

        set(uri, document).then(function () {
            console.log('Saved:', uri);

            _.each(document.headers, function (value, key) {
                res.setHeader(key, value);
            });

            res.send(document.statusCode, document.content);
        }).catch(reportAndProceed(next));
    }
};

function uriFromPath (path) {
    return path.replace(/^\//, '');
}

function domainFromPath (uri) {
    return uri.replace('http://','').replace('https://','').split(/[/?#]/)[0];
}

function originalHead (req) {
    return {
        statusCode: req.prerender.statusCode,
        headers: req.prerender.headers.reduce(function (headers, header) {
            headers[header.name] = header.value;
            return headers;
        }, {})
    };
}

function headOverrides (req) {
    var $ = cheerio.load(req.prerender.documentHTML);

    var headersOverride = {};

    $('meta[name="prerender-header"][content]').each(function (i, el) {
        var content = el.attribs.content.match(/([^:]+):\s*(.*)\s*/);
        var name = content[1];
        var value = content[2];
        headersOverride[name] = value;
    });

    var overriden = {
        headers: headersOverride
    };

    var statusCodeOverride = $('meta[name="prerender-status-code"][content]')
        .last()
        .attr('content');

    if (statusCodeOverride) {
        overriden.statusCode = parseInt(statusCodeOverride, 10);
    }

    return overriden;
}

function get (uri) {
    return table(domainFromPath(uri)).then(function (collection) {
        var findOne = Q.denodeify(collection.findOne.bind(collection));

        return findOne({ uri: uri }).then(function (item) {
            return item || Q.reject(uri + ' not found');
        });
    });
}

function set (uri, value) {
    return table(domainFromPath(uri)).then(function (collection) {
        var update = Q.denodeify(collection.update.bind(collection));

        var query = { uri: uri };
        var options = { upsert: true };

        var document = _.merge({
            uri: uri,
            date: new Date()
        }, value);

        return update(query, document, options);
    });
}

function del (uri) {
    return table(domainFromPath(uri)).then(function (collection) {
        var findAndRemove = Q.denodeify(collection.findAndRemove.bind(collection));

        return findAndRemove({ uri: uri });
    });
}

function reportAndProceed (next) {
    return function (error) {
        console.error(error.stack || error);
        next();
    };
}
<commit_msg>Added indexes on Mongodb<commit_after>'use strict';

var MongoClient = require('mongodb').MongoClient
var cheerio = require('cheerio');
var Q = require('q');
var _ = require('lodash');

var mongoUri = process.env.MONGOLAB_URI ||
    process.env.MONGOHQ_URL ||
    'mongodb://localhost/prerender';

var db = Q.denodeify(MongoClient.connect)(mongoUri);
var table = function(domain){
    return db.then(function (db) {
        return Q.denodeify(db.collection.bind(db))(domain);
    });
};

module.exports = {
    beforePhantomRequest: function(req, res, next) {
        var uri = uriFromPath(req.url);
        if (req.method === 'DELETE') {
            del(uri).then(function () {
                res.send(200, uri + ' deleted.');
            }).catch(reportAndProceed(next));
        } else if (req.method === 'PUT' || req.method === 'POST') {
            // Just pass through so that Phantom will render the page
            next();
        } else if (req.method === 'GET') {
            get(uri).then(function (document) {
                console.log('Serving from cache:', uri);

                _.each(document.headers, function (value, key) {
                    res.setHeader(key, value);
                });

                res.send(document.statusCode, document.content);
            }).catch(reportAndProceed(next));
        } else {
            res.send(400, 'Only GET, POST, PUT and DELETE methods are supported.');
        }
    },

    afterPhantomRequest: function(req, res, next) {
        var uri = uriFromPath(req.url);

        var original = originalHead(req);
        var overrides = headOverrides(req);
        var head = _.merge({}, original, overrides);

        var document = {
            content: req.prerender.documentHTML,
            statusCode: head.statusCode,
            headers: head.headers,
            originalHead: original,
            overrides: overrides
        };

        set(uri, document).then(function () {
            console.log('Saved:', uri);

            _.each(document.headers, function (value, key) {
                res.setHeader(key, value);
            });

            res.send(document.statusCode, document.content);
        }).catch(reportAndProceed(next));
    }
};

function uriFromPath (path) {
    return path.replace(/^\//, '');
}

function domainFromPath (uri) {
    return uri.replace('http://','').replace('https://','').split(/[/?#]/)[0];
}

function originalHead (req) {
    return {
        statusCode: req.prerender.statusCode,
        headers: req.prerender.headers.reduce(function (headers, header) {
            headers[header.name] = header.value;
            return headers;
        }, {})
    };
}

function headOverrides (req) {
    var $ = cheerio.load(req.prerender.documentHTML);

    var headersOverride = {};

    $('meta[name="prerender-header"][content]').each(function (i, el) {
        var content = el.attribs.content.match(/([^:]+):\s*(.*)\s*/);
        var name = content[1];
        var value = content[2];
        headersOverride[name] = value;
    });

    var overriden = {
        headers: headersOverride
    };

    var statusCodeOverride = $('meta[name="prerender-status-code"][content]')
        .last()
        .attr('content');

    if (statusCodeOverride) {
        overriden.statusCode = parseInt(statusCodeOverride, 10);
    }

    return overriden;
}

function get (uri) {
    return table(domainFromPath(uri)).then(function (collection) {
        var findOne = Q.denodeify(collection.findOne.bind(collection));

        return findOne({ uri: uri }).then(function (item) {
            return item || Q.reject(uri + ' not found');
        });
    });
}

function set (uri, value) {
    return table(domainFromPath(uri)).then(function (collection) {
        Q.denodeify(collection.ensureIndex('uri').bind(collection));
        var update = Q.denodeify(collection.update.bind(collection));

        var query = { uri: uri };
        var options = { upsert: true };

        var document = _.merge({
            uri: uri,
            date: new Date()
        }, value);

        return update(query, document, options);
    });
}

function del (uri) {
    return table(domainFromPath(uri)).then(function (collection) {
        var findAndRemove = Q.denodeify(collection.findAndRemove.bind(collection));

        return findAndRemove({ uri: uri });
    });
}

function reportAndProceed (next) {
    return function (error) {
        console.error(error.stack || error);
        next();
    };
}
<|endoftext|>


<commit_before>angular.module('ion-profile-picture', [])
  .directive('ionProfilePicture', [
    '$ionicTemplateLoader',
    '$ionicBackdrop',
    '$q',
    '$timeout',
    '$rootScope',
    '$document',
    function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $document) {
      return {
        require: '?ngModel',
        restrict: 'E',
        template: '
<input type="file" accept="image/*" capture />
',
        replace: true,
        link: function(scope, element, attrs, ngModel) {
          var $input = angular.element(element.find('input'));
          var file = undefined;

          if(!ngModel){
            console.error('ion-profile-picture:', 'Need to set ng-model');
            return false;
          }

          // all this guy does is trigger a click event on the hidden file input
          var openFileDialog = function(e){
            $input[0].click();
          };

          // every time the file gets updated, this guy does it's thing
          var onFilePick = function(e){
            var reader = new FileReader();

            reader.onload = function(_e){
              scope.$apply(function(){
                ngModel.$setViewValue(_e.target.result);
                ngModel.$render();
              })
            };

            file = e.target.files[0];

            if(file){
              // we read the data from our selected image to get the Base64
              // and use it as our element background
              reader.readAsDataURL(file);
            }
          };

          ngModel.$formatters.unshift(function (modelValue) {
              if (!modelValue) return '';
              return modelValue;
          });

          ngModel.$parsers.unshift(function (viewValue) {
              return viewValue;
          });

          ngModel.$render = function(){
            var value = ngModel.$viewValue;

            if(!value){
              element.css({
                'background-image': 'none'
              });
              element.addClass('no-picture');
            } else {
              // if our value is just a plain Base64 string, we will try
              // to be helpful and prepend the right stuff to it
              if(!value.match(/^data:.*?;base64,/i)){
                value = 'data:image/jpg;base64,' + value;
              }

              element.css({
                'background-image': 'url(' + value + ')'
              });
              element.removeClass('no-picture');
            }
          };

          element.on('click', openFileDialog);
          element.on('tap', openFileDialog);
          $input.on('change', onFilePick);
        }
      };
    }
  ]);
<commit_msg>Update ion-profile-picture.js<commit_after>angular.module('ion-profile-picture', [])
  .directive('ionProfilePicture', [
    '$ionicTemplateLoader',
    '$ionicBackdrop',
    '$q',
    '$timeout',
    '$rootScope',
    '$document',
    function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $document) {
      return {
        require: '?ngModel',
        restrict: 'E',
        template: '
<input type="file" accept="image/*" capture />
',
        replace: true,
        link: function(scope, element, attrs, ngModel) {
          var $input = angular.element(element.find('input'));
          var file = undefined;

          if(!ngModel){
            console.error('ion-profile-picture:', 'Need to set ng-model');
            return false;
          }

          // all this guy does is trigger a click event on the hidden file input
          var openFileDialog = function(e){
            $input[0].click();
          };

          // every time the file gets updated, this guy does it's thing
          var onFilePick = function(e){
            var reader = new FileReader();

            reader.onload = function(_e){
              scope.$apply(function(){
                ngModel.$setViewValue(_e.target.result);
                ngModel.$render();
              })
            };

            file = e.target.files[0];

            if(file){
              // we read the data from our selected image to get the Base64
              // and use it as our element background
              reader.readAsDataURL(file);
            }
          };

          ngModel.$formatters.unshift(function (modelValue) {
              if (!modelValue) return '';
              return modelValue;
          });

          ngModel.$parsers.unshift(function (viewValue) {
              return viewValue;
          });

          ngModel.$render = function(){
            var value = ngModel.$viewValue;

            if(!value){
              element.css({
                'background-image': 'none'
              });
              element.addClass('no-picture');
            } else {
              // if our value is just a plain Base64 string, we will try
              // to be helpful and prepend the right stuff to it
              if(!value.match(/^data:.*?;base64,/i)){
                value = 'data:image/jpg;base64,' + value;
              }

              element.css({
                'background-image': 'url(' + value + ')'
              });
              element.removeClass('no-picture');
            }
          };

          element.on('click', openFileDialog);
          $input.on('change', onFilePick);
        }
      };
    }
  ]);
<|endoftext|>


<commit_before>var request = require('request');
var restify = require('restify');
var redis = require('redis');
var url = require('url');
var redisURL = url.parse(process.env.REDISCLOUD_URL);
var client = redis.createClient(redisURL.port, redisURL.hostname, {no_ready_check: true});
client.auth(redisURL.auth.split(":")[1]);
// Update delay in seconds
var updateDelaySeconds = 60;

// Berlin city code for weather is 2950159
var weatherApiOptions = {
  url: "http://api.openweathermap.org/data/2.5/weather?id=2950159",
  headers: {"x-api-key": process.env.OWMAPIKEY}
};

function saveToRedis(wx) {
  // Set the weather (expires automatically)
  client.set("cached-wx", wx);
  client.expire("cached-wx", updateDelaySeconds);
  // Keep it in non-expiring key (for debugging)
  client.set("last-wx", wx);
}

function getWeather(callback) {
  client.get("cached-wx", function (err, reply) {
    if(reply) {
      wx = JSON.parse(reply);
      callback(wx);
      console.log("Used cache");
    }
    else {
      updateWeather(saveToRedis, callback);
      console.log("Needed to update cache");
    }
  });
}

function updateWeather(storeWx, callback) {
  request(weatherApiOptions, function (error, response, body){
    if(!error && response.statusCode == 200){
      wx = JSON.parse(body);
      storeWx(body);
      callback(wx);
    }
    else {
      console.error(error);
    }
  });
};

function isSnowing(wx) {
  return "Snow" == wx.weather[0].main;
}

function APIisSnowing(req, res, next) {
  getWeather(function (wx) {
    if(isSnowing(wx))
      res.send({isSnowing: true});
    else
      res.send({isSnowing: false});
  });
}

function APIgetRawWeather(req, res, next) {
  getWeather(function (wx){
    res.send(wx);
  });
}

function respond(req, res, next) {
  getWeather(function (wx) {
    var body = "<title>Is it snowing in Berlin?</title>
";
    if(isSnowing(wx))
      body += "Yes"
    else
      body += "No"
    body += "

Data last updated: " + new Date(wx.dt * 1000).toString() + "
"
    res.writeHead(200, {
      'Content-Length': Buffer.byteLength(body),
      'Content-Type': 'text/html'
    });
    res.write(body);
    res.end();
  })

  next();
}
console.log("Getting initial value: ");
getWeather(console.log);

var server = restify.createServer();
server.get("/", respond);
server.head("/", respond);
server.get("/api/isSnowing", APIisSnowing);
server.head("/api/isSnowing", APIisSnowing);
server.get("/api/rawWeather", APIgetRawWeather);
server.head("/api/rawWeather", APIgetRawWeather);

server.listen(process.env.PORT || 5000, function() {
  console.log('%s listening at %s', server.name, server.url);
});
<commit_msg>Since weather can have multiple values in the array, need to check all of them.<commit_after>var request = require('request');
var restify = require('restify');
var redis = require('redis');
var url = require('url');
var redisURL = url.parse(process.env.REDISCLOUD_URL);
var client = redis.createClient(redisURL.port, redisURL.hostname, {no_ready_check: true});
client.auth(redisURL.auth.split(":")[1]);
// Update delay in seconds
var updateDelaySeconds = 60;

// Berlin city code for weather is 2950159
var weatherApiOptions = {
  url: "http://api.openweathermap.org/data/2.5/weather?id=2950159",
  headers: {"x-api-key": process.env.OWMAPIKEY}
};

function saveToRedis(wx) {
  // Set the weather (expires automatically)
  client.set("cached-wx", wx);
  client.expire("cached-wx", updateDelaySeconds);
  // Keep it in non-expiring key (for debugging)
  client.set("last-wx", wx);
}

function getWeather(callback) {
  client.get("cached-wx", function (err, reply) {
    if(reply) {
      wx = JSON.parse(reply);
      callback(wx);
      console.log("Used cache");
    }
    else {
      updateWeather(saveToRedis, callback);
      console.log("Needed to update cache");
    }
  });
}

function updateWeather(storeWx, callback) {
  request(weatherApiOptions, function (error, response, body){
    if(!error && response.statusCode == 200){
      wx = JSON.parse(body);
      storeWx(body);
      callback(wx);
    }
    else {
      console.error(error);
    }
  });
};

function isSnowing(wx) {
  return wx.weather.reduce(function(previousValue, currentValue, index, array){
    // If the previousValue is true, no need to check, keep passing true
    if(previousValue) return previousValue;
    // Check if the current value shows snow
    return "Snow" == currentValue.main;
  }, false);
}

function APIisSnowing(req, res, next) {
  getWeather(function (wx) {
    if(isSnowing(wx))
      res.send({isSnowing: true});
    else
      res.send({isSnowing: false});
  });
}

function APIgetRawWeather(req, res, next) {
  getWeather(function (wx){
    res.send(wx);
  });
}

function respond(req, res, next) {
  getWeather(function (wx) {
    var body = "<title>Is it snowing in Berlin?</title>
";
    if(isSnowing(wx))
      body += "Yes"
    else
      body += "No"
    body += "

Data last updated: " + new Date(wx.dt * 1000).toString() + "
"
    res.writeHead(200, {
      'Content-Length': Buffer.byteLength(body),
      'Content-Type': 'text/html'
    });
    res.write(body);
    res.end();
  })

  next();
}
console.log("Getting initial value: ");
getWeather(console.log);

var server = restify.createServer();
server.get("/", respond);
server.head("/", respond);
server.get("/api/isSnowing", APIisSnowing);
server.head("/api/isSnowing", APIisSnowing);
server.get("/api/rawWeather", APIgetRawWeather);
server.head("/api/rawWeather", APIgetRawWeather);

server.listen(process.env.PORT || 5000, function() {
  console.log('%s listening at %s', server.name, server.url);
});
<|endoftext|>


<commit_before>function Game(player1, player2) {
	this.player1 = player1;
	this.player2 = player2;
}

Game.prototype.winner = function(){
	if (this.player1.pick === this.player2.pick) {
		return null;
	};
	if (this.PAIRS[this.player1.pick]['beats'].indexOf(this.player2.pick) !== -1 ){
		return this.player1;
	}
	else {
		return this.player2;
	};
}

Game.prototype.PAIRS = {
	rock:	{ beats: ['scissors', 'lizard'] },
	paper:  { beats: ['rock', 'spock'] },
	scissors: { beats: ['paper', 'lizard'] },
	lizard: { beats: ['spock', 'paper'] },
	spock: { beats: ['scissors', 'rock'] }
}
<commit_msg>Refactors to turn winning pairs into an object rather than an array<commit_after>function Game(player1, player2) {
	this.player1 = player1;
	this.player2 = player2;
}

Game.prototype.winner = function(){
	if (this.player1.pick === this.player2.pick) {
		return null;
	};
	if (this.PAIRS[this.player1.pick][this.player2.pick] !== undefined) {
		return this.player1;
	}
	else {
		return this.player2;
	};
}

Game.prototype.PAIRS = {
	rock:	{ scissors: '', lizard: '' },
	paper:  { rock: '', spock: '' },
	scissors: { paper: '', lizard: '' },
	lizard: { spock: '', paper: '' },
	spock: { scissors: '', rock: '' }
}
<|endoftext|>