text:
require 'sinatra'
require 'odle'

#####
# Reporting Routes
#####

config_options = JSON.parse(File.read('./config.json'))

# set the report_assessment_types for <1.2 versions of Serpico
unless config_options['report_assessment_types']
  config_options['report_assessment_types'] = ['Network Internal', 'External', 'Web application', 'Physical', 'Social engineering', 'Configuration audit']
end

# List current reports
get '/reports/list' do
  @reports = get_reports

  @admin = true if is_administrator?

  # allow the user to set their logo in the configuration options
  @logo = config_options['logo']

  haml :reports_list
end

# Create a report
get '/report/new' do
  @templates = Xslt.all
  @assessment_types = config_options['report_assessment_types']
  @languages = config_options['languages']

  @admin = true if is_administrator?
  @plugin = true if is_plugin?

  haml :new_report
end

# Create a report
post '/report/new' do
  data = url_escape_hash(request.POST)

  data['owner'] = get_username
  data['date'] = DateTime.now.strftime '%m/%d/%Y'

  @report = Reports.new(data)
  @report.scoring = set_scoring(config_options)
  @report.save

  # compensate for datamappers oddness
  @report1 = get_report(@report.id)

  redirect to("/report/#{@report.id}/edit")
end

# List attachments and provides an overview on uploaded screenshots
get '/report/:id/attachments' do
  id = params[:id]

  # Query for the first report matching the id
  @report = get_report(id)
  return 'No Such Report' if @report.nil?
  @screenshot_names_from_findings = {}
  # fetching screenshots names in findings
  findings = Findings.all(report_id: id)

  findings.each do |find|
    next unless find.overview or find.poc or find.remediation or find.notes

    text = ""
    if find.overview
      text = text + find.overview
    end
    if find.poc
      text = text + find.poc
    end
    if find.remediation
      text = text + find.remediation
    end
    if find.notes
      text = text + find.notes
    end

    @screenshot_names_from_findings[find.id] = []
    # for each finding, we extract the screenshot name in the poc field.
    # screenshot names are like this : [!!screenshotnames.png!!]
    text.to_s.split('<paragraph>').each do |pp|
      next unless pp =~ /\[\!\!/
      @screenshot_names_from_findings[find.id] << pp.split('[!!')[1].split('!!]').first
    end
  end
  @attachments = Attachments.all(report_id: id)
  # fetching screenshot names in report
  xslt = Xslt.first(report_type: @report.report_type)
  @screenshot_names_from_report = xslt.screenshot_names
  @screenshot_names_from_findings
  haml :list_attachments
end

get '/report/:id/export_attachments' do
  id = params[:id]
  rand_zip = "./tmp/#{rand(36**12).to_s(36)}.zip"
  @attachments = Attachments.all(report_id: id)

  Zip::File.open(rand_zip, Zip::File::CREATE) do |zipfile|
    @attachments.each do |attachment|
      zipfile.add(attachment.filename_location.gsub('./attachments/', ''), attachment.filename_location)
    end
  end

  send_file rand_zip, type: 'zip', filename: 'attachments.zip'
  # File.delete(rand_zip) should the temp file be deleted?
end

# Restore Attachments menu
get '/report/:id/restore_attachments' do
  @id = params[:id]
  @report = get_report(@id)
  return 'No Such Report' if @report.nil?

  haml :restore_attachments
end

post '/report/:id/restore_attachments' do
  id = params['id']
  # Not sure this is the best way to do this.
  rand_zip = "./tmp/#{rand(36**12).to_s(36)}.zip"
  File.open(rand_zip, 'wb') { |f| f.write(params[:file][:tempfile].read) }
  begin
    Zip::File.open(rand_zip) do |file|
      n = file.num_files
      n.times do |i|
        entry_name = file.get_name(i)
        file.fopen(entry_name) do |f|
          clean_name = f.name.split('.')[0]
          File.open("./attachments/#{clean_name}", 'wb') do |data|
            data << f.read
          end
        end
      end
    end
  rescue
    puts 'Not a Zip file. Please try again'
  end
  # File.delete(rand_zip) should the temp file be deleted?
  redirect to("/report/#{id}/edit")
end

# Odle based scan import
get '/report/:id/import_scan_data' do
  id = params[:id]

  # Query for the first report matching the id
  @report = get_report(id)

  return 'No Such Report' if @report.nil?

  @auto_import = config_options['auto_import']
  haml :import_scan_data
end

post '/report/:id/import_scan_data' do
  type = params[:type].to_s.downcase

  scan_xml = params[:file][:tempfile].read

  id = params[:id]

  # verify access to the report
  @report = get_report(id)
  return 'No Such Report' if @report.nil?

  if type == 'nessus'
    data = JSON.parse(Nessus.new.parse(scan_xml, config_options['threshold']))
  elsif type == 'burpv1'
    data = JSON.parse(Burp.new.parse(scan_xml, config_options['threshold']))
  elsif type == 'metasploit'
    data = JSON.parse(Metasploit.new.parse(scan_xml, config_options['threshold']))
  elsif type == 'nmap'
    data = JSON.parse(Nmap.new.parse(scan_xml, config_options['threshold']))
  else
    "Unknown type: #{type}"
  end

  vulns = import_odle(data)['findings']
  return 'Data import failed or 0 findings in import file' unless vulns

  vulns.each do |vuln|
      vuln.report_id = id
      vuln.save
  end

  # go directly to findings list menu
  @total_added = vulns.size
  @chart = config_options['chart']
  @plugin_side_menu = get_plugin_list('user')
  @findings, @dread, @cvss, @cvssv3, @risk, @riskmatrix,@nist800 = get_scoring_findings(@report)
  @cvssv2_scoring_override = if config_options.key?('cvssv2_scoring_override')
                               config_options['cvssv2_scoring_override']
                             else
                               false
                             end

  haml :findings_list
end

# upload nessus xml files to be processed
get '/report/:id/import_nessus' do
  id = params[:id]

  @nessusmap = config_options['nessusmap']

  # Query for the first report matching the id
  @report = get_report(id)

  haml :import_nessus
end

# auto add serpico findings if mapped to nessus ids
post '/report/:id/import_autoadd' do
  type = params[:type]

  xml = params[:file][:tempfile].read
  if xml =~ /^<NessusClientData_v2>/ && type == 'nessus'
    import_nessus = true
    vulns = parse_nessus_xml(xml, config_options['threshold'])
  elsif xml =~ /^<issues burpVersion/ && type == 'burp'
    import_burp = true
    vulns = parse_burp_xml(xml)
  else
    return 'File does not contain valid XML import data'
  end

  # reject if the file is above a certain limit
  # if params[:file][:tempfile].size > 1000000
  #        return "File too large. 1MB limit"
  # end
  # Check for kosher name in report name
  id = params[:id]

  add_findings = []
  dup_findings = []
  autoadd_hosts = {}

  # Query for the first report matching the report_name
  @report = get_report(id)

  return 'No Such Report' if @report.nil?

  # load all findings
  @findings = TemplateFindings.all(order: [:title.asc])

  # parse nessus xml into hash
  # nessus_vulns = parse_nessus_xml(nessus_xml)

  # determine findings to add from vuln data
  # host/ip is key, value is array of vuln ids
  vulns.keys.each do |i|
    vulns[i].each do |v|
      # if serpico finding id maps to nessus/burp plugin id, add to report
      if import_nessus
        @mappings = NessusMapping.all(pluginid: v)
      elsif import_burp
        @mappings = BurpMapping.all(pluginid: v)
      end

      # add affected hosts for each finding
      next unless @mappings
      @mappings.each do |m|
        if autoadd_hosts[m.templatefindings_id]
          # only one host/url per finding (regardless of ports and urls). this should change in the future
          unless autoadd_hosts[m.templatefindings_id].include?(i)
            autoadd_hosts[m.templatefindings_id] << i
          end
        else
          autoadd_hosts[m.templatefindings_id] = []
          autoadd_hosts[m.templatefindings_id] << i
        end
        add_findings << m.templatefindings_id
      end
    end
  end

  add_findings = add_findings.uniq

  # create new findings from an import
  # TODO: This will duplicate if the user already has a nessus id mapped
  if config_options['auto_import']
    vulns['findings'].each do |vuln|
      vuln.report_id = id
      vuln.save
    end
  end

  if add_findings.empty?
    redirect to("/report/#{id}/findings")
  else
    @autoadd = true

    add_findings.each do |finding|
      # if the finding already exists in the report dont add
      currentfindings = Findings.all(report_id: id)
      currentfindings.each do |cf|
        next unless cf.master_id == finding.to_i
        dup_findings << finding.to_i unless dup_findings.include?(finding.to_i)
        add_findings.delete(finding.to_i)
      end
    end
    @autoadd_hosts = autoadd_hosts
    @dup_findings = dup_findings.uniq
    @autoadd_findings = add_findings
  end
  haml :findings_add
end

# upload burp xml files to be processed
get '/report/:id/import_burp' do
  id = params[:id]

  @burpmap = config_options['burpmap']

  # Query for the first report matching the id
  @report = get_report(id)

  haml :import_burp
end

# Upload attachment menu
get '/report/:id/upload_attachments' do
  id = params[:id]
  @no_file = params[:no_file]

  # Query for the first report matching the id
  @report = get_report(id)

  return 'No Such Report' if @report.nil?

  @attachments = Attachments.all(report_id: id)
  haml :upload_attachments
end

post '/report/:id/upload_attachments' do
  id = params[:id]

  # Query for the first report matching the id
  @report = get_report(id)

  return 'No Such Report' if @report.nil?

  if params[:files].nil?
    redirect to("/report/#{id}/upload_attachments?no_file=1")
  end

  params['files'].map do |upf|
    # We use a random filename
    rand_file = "./attachments/#{rand(36**36).to_s(36)}"

    # reject if the file is above a certain limit
    return 'File too large. 100MB limit' if upf[:tempfile].size > 100_000_000

    # open up a file handle and write the attachment
    File.open(rand_file, 'wb') { |f| f.write(upf[:tempfile].read) }

    # delete the file data from the attachment
    datax = {}
    # to prevent traversal we hardcode this
    datax['filename_location'] = rand_file.to_s
    datax['filename'] = upf[:filename]
    datax['description'] = CGI.escapeHTML(upf[:filename]).tr(' ', '_').tr('/', '_').tr('\\', '_').tr('`', '_')
    datax['report_id'] = id
    datax['appendice'] = params[:appendice]
    data = url_escape_hash(datax)

    @attachment = Attachments.new(data)
    @attachment.save
  end
  redirect to("/report/#{id}/attachments")
end

get '/report/:id/export_attachments' do
  id = params[:id]
  rand_zip = "./tmp/#{rand(36**12).to_s(36)}.zip"
  @attachments = Attachments.all(report_id: id)

  Zip::File.open(rand_zip, Zip::File::CREATE) do |zipfile|
    @attachments.each do |attachment|
      zipfile.add(attachment.filename_location.gsub('./attachments/', ''), attachment.filename_location)
    end
  end

  send_file rand_zip, type: 'zip', filename: 'attachments.zip'
  # File.delete(rand_zip) should the temp file be deleted?
end

# display attachment
get '/report/:id/attachments/:att_id' do
  id = params[:id]

  # Query for the first report matching the id
  @report = get_report(id)

  return 'No Such Report' if @report.nil?

  @attachment = Attachments.first(report_id: id, id: params[:att_id])
  send_file @attachment.filename_location, filename: @attachment.filename.to_s
end

# Delete an attachment
get '/report/:id/attachments/delete/:att_id' do
  id = params[:id]

  # Query for the first report matching the id
  @report = get_report(id)

  return 'No Such Report' if @report.nil?
  params[:att_id].split(',').each do |current_id|
    @attachment = Attachments.first(report_id: id, id: current_id)
    return "No Such Attachment : #{current_id}" if @attachment.nil?
    if File.file?(@attachment.filename_location)
      File.delete(@attachment.filename_location)
    end
    # delete the entries
    @attachment.destroy
  end
  redirect to("/report/#{id}/attachments")
end

# Delete a report
get '/report/remove/:id' do
  id = params[:id]
  params[:id].split(',').each do |current_id|
    report = get_report(current_id)
    return "No Such Report : #{current_id}" if report.nil?
    # get all findings and udos associated with the report
    findings = Findings.all(report_id: id)
    udos = UserDefinedObjects.all(report_id: id)

    # Notify the plugins that the report will be deleted
    PluginNotifier.instance.notify_report_deleted(report)

    # delete the entries
    findings.destroy
    udos.destroy
    report.destroy
  end
  serpico_log("Report deleted, Report #{id}")
  redirect to('/reports/list')
end

# Edit the Report's main information; Name, Consultant, etc.
get '/report/:id/edit' do
  id = params[:id]

  # Query for the first report matching the report_name
  @report = get_report(id)
  @templates = Xslt.all(order: [:report_type.asc])
  @plugin_side_menu = get_plugin_list('user')
  @assessment_types = config_options['report_assessment_types']
  @languages = config_options['languages']
  @risk_scores = %w[Risk DREAD CVSS CVSSv3 RiskMatrix NIST800]

  return 'No Such Report' if @report.nil?

  @report.update(scoring: set_scoring(config_options)) unless @report.scoring

  haml :report_edit
end

# Edit the Report's main information; Name, Consultant, etc.
get '/report/:id/additional_features' do
  id = params[:id]

  # Query for the first report matching the report_name
  @report = get_report(id)

  return 'No Such Report' if @report.nil?

  haml :additional_features
end

# Edit a report
post '/report/:id/edit' do
  id = params[:id]

  data = url_escape_hash(request.POST)
  @report = get_report(id)

  unless @report.update(data)
    error = ""
    @report.errors.each do |f|
      error = error + f.to_s() + "
"
    end
    return "
The following error(s) were found while trying to update :
#{error}"
  end

  redirect to("/report/#{id}/edit")
end

# Manage user defined objects. User can see all UDOs from here.
get '/report/:id/udo/manage' do
  # if a udo has just been created, we add a message
  @id = params[:id]
  @report = get_report(@id)
  return 'No Such Report' if @report.nil?

  if params[:created_id]
    @udo_template_created = UserDefinedObjectTemplates.get(params[:created_id])
  end
  @udos_templates = UserDefinedObjectTemplates.all

  haml :user_defined_object_manage
end

# Create new user defined objects. Get => the user chose the udo values
get '/report/:id/udo/:udo_template_id/create' do
  @id = params[:id]
  @report = get_report(@id)
  return 'No Such Report' if @report.nil?

  @udo_template = UserDefinedObjectTemplates.get(params[:udo_template_id])
  return 'no Such UDO Template' if @udo_template.nil?
  @udo_template_properties = JSON.parse(@udo_template.udo_properties)
  haml :user_defined_object_create
end

# Create new user defined objects. Post => the UDO is stored in database
post '/report/:id/udo/:udo_template_id/create' do
  data = url_escape_hash(request.POST)
  id = params[:id]
  report = get_report(id)
  return 'No Such Report' if report.nil?
  udo_template_id = params[:udo_template_id]
  @udo_template = UserDefinedObjectTemplates.get(udo_template_id)
  return 'no Such UDO Template' if @udo_template.nil?
  # we create the udo
  @udo = UserDefinedObjects.new
  @udo.type = @udo_template.type
  @udo.report_id = id
  @udo.template_id = udo_template_id
  # we extract the properties from the posted data
  udo_properties = {}
  data.each do |post_param, post_value|
    if post_param =~ /param_/
      if post_value !~ /\<paragraph\>/
        udo_properties[post_param.split('_')[1].downcase] = "<paragraph>#{post_value}</paragraph>"
      else
        udo_properties[post_param.split('_')[1].downcase] = post_value
      end
    end
  end
  @udo.udo_properties = udo_properties.to_json
  @udo.save
  # we go back to the manage panel after creating the udo
  redirect to("/report/#{id}/udo/manage?created_id=#{@udo_template.id}")
end

# Edit an UDO. get => user chose the values
get '/report/:id/udo/:udo_id/edit' do
  id = params[:id]
  udo_id = params[:udo_id]
  @report = get_report(id)
  return 'No Such Report' if @report.nil?

  # By asking only udos for the report we have access to, we're safe from
  # edit by arbitrary user
  udos = UserDefinedObjects.all(report_id: id, id: udo_id)
  return 'No Such UDO for this report' if udos.empty?
  @udo_to_edit = UserDefinedObjects.new
  # there will always be only one udo in this collecion
  udos.each do |udo_to_edit|
    @udo_to_edit = udo_to_edit
  end
  # udo template linked to the udo to edit. We want the template in case the admin changed the properties
  @udo_template = UserDefinedObjectTemplates.get(@udo_to_edit.template_id)
  @udo_template_properties = JSON.parse(@udo_template.udo_properties)
  @udo_to_edit_properties = JSON.parse(@udo_to_edit.udo_properties)

  haml :user_defined_object_edit
end

# Edit an UDO. Post => value stored in db
post '/report/:id/udo/:udo_id/edit' do
  data = url_escape_hash(request.POST)
  id = params[:id]
  udo_id = params[:udo_id]
  report = get_report(id)
  return 'No Such Report' if report.nil?
  # By asking only udos for the report we have access to, we're safe from
  # edit by arbitrary user
  udos = UserDefinedObjects.all(report_id: id, id: udo_id)
  return 'No Such UDO for this report' if udos.empty?
  edited_udo = UserDefinedObjects.new
  # there will always be only one udo in this collecion
  udos.each do |udo_to_edit|
    edited_udo = udo_to_edit
  end
  # we extract the properties from the posted data
  udo_new_properties = {}
  data.each do |post_param, post_value|
    if post_param =~ /param_/
      if post_value !~ /\<paragraph\>/
        udo_new_properties[post_param.split('_')[1].downcase] = "<paragraph>#{post_value}</paragraph>"
      else
        udo_new_properties[post_param.split('_')[1].downcase] = post_value
      end
    end
  end
  edited_udo.udo_properties = udo_new_properties.to_json
  edited_udo.save
  redirect to("/report/#{id}/udo/manage")
end

# Delete an UDO
get '/report/:id/udo/:udo_id/delete' do
  @id = params[:id]
  udo_id = params[:udo_id]
  report = get_report(@id)
  return 'No Such Report' if report.nil?
  # By asking only udos for the report we have access to, we're safe from
  # delete by arbitrary user
  udos = UserDefinedObjects.all(report_id: @id, id: udo_id)
  return 'No Such UDO for this report' if udos.empty?
  # there will always be only one udo in this collecion
  udos.each(&:destroy)
  redirect to("/report/#{@id}/udo/manage")
end

# Edit user defined variable
get '/report/:id/user_defined_variables' do
  id = params[:id]
  @report = get_report(id)

  if @report.user_defined_variables
    @user_variables = JSON.parse(@report.user_defined_variables)

    # add in the global UDV from config
    if !config_options['user_defined_variables'].empty? && !@user_variables.include?(config_options['user_defined_variables'][0])
      config_options['user_defined_variables'].each do |key, _value|
        @user_variables.store(key, '')
      end
    end

    @user_variables.each do |k, v|
      @user_variables[k] = meta_markup(v) if v
    end
  else
    @user_variables = config_options['user_defined_variables']
  end

  haml :user_defined_variable
end

# TODO: this route needs proper comments, it is very confusing
post '/report/:id/user_defined_variables' do
  data = url_escape_hash(request.POST)

  data.each do |k, v|
    if k =~ /variable_data/ && (v !~ /\<paragraph\>/)
      data[k.strip] = "<paragraph>#{v}</paragraph>"
    end
  end

  variable_hash = {}
  data.each do |k, v|
    if k =~ /variable_name/
      key = k.split('variable_name_').last.split('_').first.strip

      v = v.tr('%', '_').gsub('"', "'").gsub('&', '').gsub('>', '').gsub('<', '')

      variable_hash["#{key}%#{v}"] = 'DEFAULT'

    end

    # TODO are the next few lines ever hit?
    next unless k =~ /variable_data/
    key = k.split('variable_data_').last.split('_').first.strip

    variable_hash.each do |k1, _v1|
      next unless k1 =~ /%/
      kk = k1.split('%')
      variable_hash[k1] = v if kk.first == key
    end
  end

  q = variable_hash.clone
  variable_hash.each do |k, v|
    next unless k =~ /%/
    if k.split('%').size == 1
      q.delete(k)
    else
      q[k.split('%').last.strip] = v
      q.delete(k)
    end
  end
  variable_hash = q

  id = params[:id]
  @report = get_report(id)

  @report.user_defined_variables = variable_hash.to_json
  @report.save
  redirect to("/report/#{id}/user_defined_variables")
end

# Findings List Menu
get '/report/:id/findings' do
  @chart = config_options['chart']
  @report = true
  id = params[:id]

  # Query for the first report matching the report_name
  @report = get_report(id)
  @plugin_side_menu = get_plugin_list('user')

  return 'No Such Report' if @report.nil?

  @report.update(scoring: set_scoring(config_options)) unless @report.scoring

  @findings, @dread, @cvss, @cvssv3, @risk, @riskmatrix,@nist800 = get_scoring_findings(@report)

  @cvssv2_scoring_override = if config_options.key?('cvssv2_scoring_override')
                               config_options['cvssv2_scoring_override']
                             else
                               false
                             end
  haml :findings_list
end

# Generate a status report from the current findings
get '/report/:id/status' do
  id = params[:id]

  # Query for the report
  @report = get_report(id)

  return 'No Such Report' if @report.nil?

  @findings, @dread, @cvss, @cvssv3, @risk, @riskmatrix,@nist800 = get_scoring_findings(@report)

  ## We have to do some hackery here for wordml
  findings_xml = ''
  findings_xml << '<findings_list>'
  @findings.each do |finding|
    ### Let's find the diff between the original and the new overview and remediation
    master_finding = TemplateFindings.first(id: finding.master_id)

    findings_xml << finding.to_xml
  end
  findings_xml << '</findings_list>'

  findings_xml = meta_markup_unencode(findings_xml, @report)

  report_xml = findings_xml.to_s

  xslt_elem = Xslt.first(status_template: true)

  if xslt_elem

    # Push the finding from XML to XSLT
    xslt = Nokogiri::XSLT(File.read(xslt_elem.xslt_location))

    docx_xml = xslt.transform(Nokogiri::XML(report_xml))

    # We use a temporary file with a random name
    rand_file = "./tmp/#{rand(36**12).to_s(36)}.docx"

    # Create a temporary copy of the finding_template
    FileUtils.copy_file(xslt_elem.docx_location, rand_file)

    ### IMAGE INSERT CODE
    if docx_xml.to_s =~ /\[!!/
      # first we read in the current [Content_Types.xml]
      content_types = read_rels(rand_file, '[Content_Types].xml')

      # add the png and jpg handling to end of content types document
      if content_types !~ /image\/jpg/
        content_types = content_types.sub('</Types>', '<Default Extension="jpg" ContentType="image/jpg"/></Types>')
      end
      if content_types !~ /image\/png/
        content_types = content_types.sub('</Types>', '<Default Extension="png" ContentType="image/png"/></Types>')
      end
      if content_types !~ /image\/jpeg/
        content_types = content_types.sub('</Types>', '<Default Extension="jpeg" ContentType="image/jpeg"/></Types>')
      end

      docx_modify(rand_file, content_types, '[Content_Types].xml')

      # replace all [!! image !!] in the document
      imgs = docx_xml.to_s.split('[!!')
      docx = imgs.first
      imgs.delete_at(0)

      imgs.each do |image_i|
        name = image_i.split('!!]').first.delete(' ')
        end_xml = image_i.split('!!]').last

        # search for the image in the attachments
        image = Attachments.first(description: name, report_id: id)

        # tries to prevent breakage in the case image dne
        if image
          docx = image_insert(docx, rand_file, image, end_xml)
        else
          docx = docx.sub(/<w:p[^\>]*?>((?<!<w:p[ |>]).)*\z/m, '')
          end_xml = end_xml.sub(/^<\/w:t>.*?<\/w:r>.*?<\/w:p>/m, '')
          docx << end_xml
        end
      end

    else
      # no images in finding
      docx = docx_xml.to_s
    end
    #### END IMAGE INSERT CODE

    docx_modify(rand_file, docx, 'word/document.xml')

    send_file rand_file, type: 'docx', filename: 'status.docx'

  else
    "You don't have a Finding Template (did you delete the temp?) -_- ... If you're an admin go to here to add one."
  end
end

# Add a finding to the report
get '/report/:id/findings_add' do
  # Check for kosher name in report name
  id = params[:id]

  # Query for the first report matching the report_name
  @report = get_report(id)

  return 'No Such Report' if @report.nil?

  @languages = config_options['languages']

  # Query for all Findings
  @findings = TemplateFindings.all(approved: true, order: [:title.asc])

  haml :findings_add
end

# Add a finding to the report
post '/report/:id/findings_add' do
  # Check for kosher name in report name
  id = params[:id]

  # Query for the first report matching the report_name
  @report = get_report(id)

  return 'No Such Report' if @report.nil?

  hosts = ''

  redirect to("/report/#{id}/findings") unless params[:finding]

  params[:finding].each do |finding|
    templated_finding = TemplateFindings.first(id: finding.to_i)

    templated_finding.id = nil
    attr = templated_finding.attributes
    attr.delete(:approved)
    attr['master_id'] = finding.to_i
    @newfinding = Findings.new(attr)
    @newfinding.report_id = id

    # because of multiple scores we need to make sure all are set
    # => leave it up to the user to make the calculation if they switch mid report
    @newfinding.dread_total = 0 if @newfinding.dread_total.nil?
    @newfinding.nist800_total = 0 if @newfinding.nist800_total == nil
    @newfinding.cvss_total = 0  if @newfinding.cvss_total.nil?
    @newfinding.risk = 0 if @newfinding.risk.nil?

    @newfinding.save
  end

  # if we have hosts add them to the findings too
  params[:finding].each do |number|
    # if there are hosts to add with a finding they'll have a param syntax of "findingXXX=ip1,ip2,ip3"
    @findingnum = "finding#{number}"
    # TODO: merge with existing hosts (if any) probably should handle this host stuff in the db
    finding = Findings.first(report_id: id, master_id: number.to_i)

    unless params[@findingnum.to_s].nil?
      params[@findingnum.to_s].split(',').each do |ip|
        # TODO: this is dirty. also should support different delimeters instead of just newline
        hosts << '<paragraph>' + ip.to_s + '</paragraph>'
      end

      finding.affected_hosts = hosts
      hosts = ''
    end
    finding.save
  end

  serpico_log("#{@newfinding.title} added to report #{id}")

  @findings, @dread, @cvss, @cvssv3, @risk, @riskmatrix,@nist800 = get_scoring_findings(@report)

  haml :findings_list
end

# Create a new finding in the report
get '/report/:id/findings/new' do
  # Query for the first report matching the report_name
  @report = get_report(params[:id])
  return 'No Such Report' if @report.nil?

  # attachments autocomplete work
  temp_attaches = Attachments.all(report_id: params[:id])
  @attaches = []
  temp_attaches.each do |ta|
    next unless ta.description =~ /\.png$/i || ta.description =~ /\.jpg$/i || ta.description =~ /\.jpeg$/i
    @attaches.push(ta.description)
  end

  @findings, @dread, @cvss, @cvssv3, @risk, @riskmatrix,@nist800 = get_scoring_findings(@report)

  haml :create_finding
end

# Create the finding in the DB
post '/report/:id/findings/new' do
  error = mm_verify(request.POST)
  return error if error.size > 1
  data = url_escape_hash(request.POST)

  id = params[:id]
  @report = get_report(id)
  return 'No Such Report' if @report.nil?

  if @report.scoring.casecmp('dread').zero?
    data['dread_total'] = data['damage'].to_i + data['reproducability'].to_i + data['exploitability'].to_i + data['affected_users'].to_i + data['discoverability'].to_i
  elsif @report.scoring.casecmp('cvss').zero?
    data = cvss(data, false)
  elsif @report.scoring.casecmp('cvssv3').zero?
    data = cvss(data, true)
  elsif(@report.scoring.downcase == "nist800")
    # call nist800 helper function
    data = nist800(data)
  end

  data['report_id'] = id

  @finding = Findings.new(data)
  @finding.save

  # because of multiple scores we need to make sure all are set
  # => leave it up to the user to make the calculation if they switch mid report

  @finding.dread_total = 0 if @finding.dread_total.nil?
  @finding.nist800_total = 0 if @finding.nist800_total == nil
  @finding.cvss_total = 0 if @finding.cvss_total.nil?
  @finding.risk = 0 if @finding.risk.nil?
  @finding.save

  # for a parameter_pollution on report_id
  redirect to("/report/#{id}/findings")
end

# Edit the finding in a report
get '/report/:id/findings/:finding_id/edit' do
  id = params[:id]

  # Query for the first report matching the report_name
  @report = get_report(id)
  @states = config_options['finding_states']

  return 'No Such Report' if @report.nil?

  finding_id = params[:finding_id]

  # Query for all Findings
  @finding = Findings.first(report_id: id, id: finding_id)

  return 'No Such Finding' if @finding.nil?

  # attachments autocomplete work
  temp_attaches = Attachments.all(report_id: id)
  @attaches = []
  temp_attaches.each do |ta|
    next unless ta.description =~ /\.png$/i || ta.description =~ /\.jpg$/i || ta.description =~ /\.jpeg$/i
    @attaches.push(ta.description)
  end

  @findings, @dread, @cvss, @cvssv3, @risk, @riskmatrix,@nist800 = get_scoring_findings(@report)

  haml :findings_edit
end

# Edit a finding in the report
post '/report/:id/findings/:finding_id/edit' do
  # Check for kosher name in report name
  id = params[:id]

  # Query for the report
  @report = get_report(id)
  @states = config_options['finding_states']

  return 'No Such Report' if @report.nil?

  finding_id = params[:finding_id]

  # Query for all Findings
  @finding = Findings.first(report_id: id, id: finding_id)

  return 'No Such Finding' if @finding.nil?

  error = mm_verify(request.POST)
  return error if error.size > 1
  data = url_escape_hash(request.POST)

  data['title'] = data['title']

  if @states
      data['state'] =  @states.find_index(data['state'])
  end

  if @report.scoring.casecmp('dread').zero?
    data['dread_total'] = data['damage'].to_i + data['reproducability'].to_i + data['exploitability'].to_i + data['affected_users'].to_i + data['discoverability'].to_i
  elsif @report.scoring.casecmp('cvss').zero?
    data = cvss(data, false)
  elsif @report.scoring.casecmp('cvssv3').zero?
    data = cvss(data, true)
  elsif(@report.scoring.downcase == "nist800")
    # call nist800 helper function
    data = nist800(data)
  end

  # Update the finding with templated finding stuff
  unless @finding.update(data)
    error = ""
    @finding.errors.each do |f|
      error = error + f.to_s() + "
"
    end
    return "
The following error(s) were found while trying to update :
#{error}"
  end

  # because of multiple scores we need to make sure all are set
  # => leave it up to the user to make the calculation if they switch mid report
  @finding.dread_total = 0 if @finding.dread_total.nil?
  @finding.nist800_total = 0 if @finding.nist800_total == nil
  @finding.cvss_total = 0 if @finding.cvss_total.nil?
  @finding.risk = 0 if @finding.risk.nil?
  @finding.save

  redirect to("/report/#{id}/findings")
end

# Upload a finding from a report into the database
get '/report/:id/findings/:finding_id/upload' do
  # Check for kosher name in report name
  id = params[:id]

  # Query for the report
  @report = get_report(id)

  return 'No Such Report' if @report.nil?

  finding_id = params[:finding_id]

  # Query for the finding
  @finding = Findings.first(report_id: id, id: finding_id)

  return 'No Such Finding' if @finding.nil?

  # We can't create a direct copy b/c TemplateFindings doesn't have everything findings does
  # Check model/master.rb to compare
  attr = {
    title: @finding.title,
    damage: @finding.damage,
    reproducability: @finding.reproducability,
    exploitability: @finding.exploitability,
    affected_users: @finding.affected_users,
    discoverability: @finding.discoverability,
    dread_total: @finding.dread_total,
    nist800_total: @finding.nist800_total,
    cvss_base: @finding.cvss_base,
    cvss_impact: @finding.cvss_impact,
    cvss_exploitability: @finding.cvss_exploitability,
    cvss_temporal: @finding.cvss_temporal,
    cvss_environmental: @finding.cvss_environmental,
    cvss_modified_impact: @finding.cvss_modified_impact,
    cvss_total: @finding.cvss_total,
    effort: @finding.effort,
    type: @finding.type,
    overview: @finding.overview,
    poc: @finding.poc,
    remediation: @finding.remediation,
    approved: false,
    references: @finding.references,
    risk: @finding.risk,
    attack_vector: @finding.attack_vector,
    attack_complexity: @finding.attack_complexity,
    privileges_required: @finding.privileges_required,
    user_interaction: @finding.user_interaction,
    scope_cvss: @finding.scope_cvss,
    confidentiality: @finding.confidentiality,
    integrity: @finding.integrity,
    availability: @finding.availability,
    exploit_maturity: @finding.exploit_maturity,
    remeditation_level: @finding.remeditation_level,
    report_confidence: @finding.report_confidence,
    confidentiality_requirement: @finding.confidentiality_requirement,
    integrity_requirement: @finding.integrity_requirement,
    availability_requirement: @finding.availability_requirement,
    mod_attack_vector: @finding.mod_attack_vector,
    mod_attack_complexity: @finding.mod_attack_complexity,
    mod_privileges_required: @finding.mod_privileges_required,
    mod_user_interaction: @finding.mod_user_interaction,
    mod_scope: @finding.mod_scope,
    mod_confidentiality: @finding.mod_confidentiality,
    mod_integrity: @finding.mod_integrity,
    mod_availability: @finding.mod_availability,
    cvss_base_score: @finding.cvss_base_score,
    cvss_impact_score: @finding.cvss_impact_score,
    cvss_mod_impact_score: @finding.cvss_mod_impact_score,
    severity: @finding.severity,
    likelihood: @finding.likelihood
  }

  @new_finding = TemplateFindings.new(attr)
  @new_finding.save

  serpico_log("Finding #{@finding.title} from Report #{id} uploaded to the template database")

  redirect to("/report/#{id}/findings")
end

# Remove a finding from the report
get '/report/:id/findings/remove/:finding_id' do
  # Check for kosher name in report name
  id = params[:id]

  # Query for the report
  @report = get_report(id)

  return 'No Such Report' if @report.nil?

  finding_id = params[:finding_id]

  params[:finding_id].split(',').each do |current_id|
    finding = Findings.first(report_id: id, id: current_id)
    return "No Such Finding : #{current_id}" if finding.nil?
    # delete the entries
    finding.destroy
    serpico_log("#{finding.title} deleted from report #{id}")
  end
  redirect to("/report/#{id}/findings")
end

# preview a finding
get '/report/:id/findings/:finding_id/preview' do
  id = params[:id]

  # Query for the report
  @report = get_report(id)

  return 'No Such Report' if @report.nil?

  # Query for the Finding
  @finding = Findings.first(report_id: id, id: params[:finding_id])

  return 'No Such Finding' if @finding.nil?

  # this flags edited findings
  if @finding.master_id
    master = TemplateFindings.first(id: @finding.master_id)
    @finding.overview = compare_text(@finding.overview, master.overview)
  end

  ## We have to do some hackery here for wordml
  findings_xml = ''
  findings_xml << '<findings_list>'
  findings_xml << @finding.to_xml
  findings_xml << '</findings_list>'

  findings_xml = meta_markup_unencode(findings_xml, @report)

  report_xml = findings_xml.to_s

  xslt_elem = Xslt.first(finding_template: true)

  if xslt_elem

    # Push the finding from XML to XSLT
    xslt = Nokogiri::XSLT(File.read(xslt_elem.xslt_location))

    docx_xml = xslt.transform(Nokogiri::XML(report_xml))

    # We use a temporary file with a random name
    rand_file = "./tmp/#{rand(36**12).to_s(36)}.docx"

    # Create a temporary copy of the finding_template
    FileUtils.copy_file(xslt_elem.docx_location, rand_file)

    ### IMAGE INSERT CODE
    if docx_xml.to_s =~ /\[!!/
      # first we read in the current [Content_Types.xml]
      content_types = read_rels(rand_file, '[Content_Types].xml')

      # add the png and jpg handling to end of content types document
      if content_types !~ /image\/jpg/
        content_types = content_types.sub('</Types>', '<Default Extension="jpg" ContentType="image/jpg"/></Types>')
      end
      if content_types !~ /image\/png/
        content_types = content_types.sub('</Types>', '<Default Extension="png" ContentType="image/png"/></Types>')
      end
      if content_types !~ /image\/jpeg/
        content_types = content_types.sub('</Types>', '<Default Extension="jpeg" ContentType="image/jpeg"/></Types>')
      end

      docx_modify(rand_file, content_types, '[Content_Types].xml')

      # replace all [!! image !!] in the document
      imgs = docx_xml.to_s.split('[!!')
      docx = imgs.first
      imgs.delete_at(0)

      imgs.each do |image_i|
        name = image_i.split('!!]').first.delete(' ')
        end_xml = image_i.split('!!]').last

        # search for the image in the attachments
        image = Attachments.first(description: name, report_id: id)

        # tries to prevent breakage in the case image dne
        if image
          # inserts the image into the doc
          docx = image_insert(docx, rand_file, image, end_xml)
        else
          docx = docx.sub(/<w:p[^\>]*?>((?<!<w:p[ |>]).)*\z/m, '')
          end_xml = end_xml.sub(/^<\/w:t>.*?<\/w:r>.*?<\/w:p>/m, '')
          docx << end_xml
        end
      end

    else
      # no images in finding
      docx = docx_xml.to_s
    end
    #### END IMAGE INSERT CODE

    docx_modify(rand_file, docx, 'word/document.xml')

    send_file rand_file, type: 'docx', filename: "#{@finding.title}.docx"
  else

    "You don't have a Finding Template (did you delete the default one?) -_- ... If you're an admin go to here to add one."

  end
end

# Generate the report
get '/report/:id/generate' do
  id = params[:id]

  # Query for the report
  @report = get_report(id)

  return 'No Such Report' if @report.nil?

  @report.update(scoring: set_scoring(config_options)) unless @report.scoring

  user = User.first(username: get_username)

  if user
    @report.consultant_name = user.consultant_name
    @report.consultant_phone = user.consultant_phone
    @report.consultant_email = user.consultant_email
    @report.consultant_title = user.consultant_title
    @report.consultant_company = user.consultant_company

  else
    @report.consultant_name = ''
    @report.consultant_phone = ''
    @report.consultant_email = ''
    @report.consultant_title = ''
    @report.consultant_company = ''

  end
  @report.save

  @findings, @dread, @cvss, @cvssv3, @risk, @riskmatrix,@nist800 = get_scoring_findings(@report)

  ## We have to do some hackery here for wordml
  findings_xml = ''
  findings_xml << '<findings_list>'

  finding_number = 1

  @findings.each do |finding|
    finding.finding_number = finding_number
    # This flags new or edited findings
    if finding.master_id
      master = TemplateFindings.first(id: finding.master_id)
      if master
        finding.overview = compare_text(finding.overview, master.overview)
        finding.remediation = compare_text(finding.remediation, master.remediation)
      else
        finding.overview = compare_text(finding.overview, nil)
        finding.remediation = compare_text(finding.remediation, nil)
      end
    else
      finding.overview = compare_text(finding.overview, nil)
      finding.remediation = compare_text(finding.remediation, nil)
    end
    findings_xml << finding.to_xml
    finding_number += 1
  end

  findings_xml << '</findings_list>'

  # Replace the stub elements with real XML elements
  findings_xml = meta_markup_unencode(findings_xml, @report)

  # check if the report has user_defined variables
  if @report.user_defined_variables
    # we need the user defined variables in xml
    udv_hash = JSON.parse(@report.user_defined_variables)
  end

  # adding the udvs to the XML

  # update udv_hash with findings totals
  udv_hash = add_findings_totals(udv_hash, @findings, config_options)

  udv = "<udv>\n"
  udv_hash.each do |key, value|
    udv << "<#{key}>"
    udv << value.to_s
    udv << "</#{key}>\n"
  end
  udv << "</udv>\n"

  # adding the udos to the XML
  udo_xml = "<udo>\n"
  udo_templates = UserDefinedObjectTemplates.all
  udo_templates.each do |udo_template|
    # we only add the udos that are linked to the current report, and linked to its respective template
    udos = UserDefinedObjects.all(report_id: @report.id, template_id: udo_template.id)
    udos.each do |udo|
      udo_xml << "\t<#{udo_template.type.downcase.tr(' ', '_')}>\n"
      properties = JSON.parse(udo.udo_properties)
      properties.each do |prop, value|
        udo_xml << "\t\t<#{prop.downcase.tr(' ', '_')}>"
        udo_xml << value.to_s
        udo_xml << "</#{prop.downcase.tr(' ', '_')}>\n"
      end
      udo_xml << "\t</#{udo_template.type.downcase.tr(' ', '_')}>\n"
    end
  end
  udo_xml << "</udo>\n"
  # if msf connection up, we add services and hosts to the xml
  services_xml = ''
  if (msfsettings = RemoteEndpoints.first(report_id: @report.id))
    if (rpc = msfrpc(@report.id))
      res = rpc.call('console.create')
      rpc.call('db.set_workspace', msfsettings.workspace)
      # We create the XML from the opened services. onlyup undocumented but it does exist
      res = rpc.call('db.services', limit: 10_000, only_up: true)
      msfservices = res['services']
      services_xml_raw = Nokogiri::XML::Builder.new do |xml|
        xml.services do
          msfservices.each do |msfservice|
            xml.service do
              msfservice.each do |key, value|
                xml.send "#{key}_", value
              end
            end
          end
        end
      end
      services_xml = services_xml_raw.doc.root.to_xml
      # we create the XML from the hosts found.
      res = rpc.call('db.hosts', limit: 10_000)
      msfhosts = res['hosts']
      hosts_xml_raw = Nokogiri::XML::Builder.new do |xml|
        xml.hosts do
          msfhosts.each do |msfhost|
            xml.host do
              msfhost.each do |key, value|
                xml.send "#{key}_", value
              end
            end
          end
        end
      end
      hosts_xml = hosts_xml_raw.doc.root.to_xml
    end
  end
  # we add the xml from the attachments the user added
  all_appendices_xml = "<appendices>\n"
  all_appendices = Attachments.all(report_id: id, appendice: true)
  all_appendices.each do |appendice|
    next unless File.file?(appendice.filename_location)
    # the filename without the extension becomes the xml tag
    appendice_xml = "<#{appendice.filename.split('.')[0]}>"
    appendice_xml += Nokogiri::XML(File.open(appendice.filename_location).read).root.to_xml
    appendice_xml += "</#{appendice.filename.split('.')[0]}>"
    all_appendices_xml += appendice_xml.to_s

  end
  all_appendices_xml += "</appendices>\n"

  # We notify all the plugins to get their output and add it to the report xml
  plugins_xml = PluginNotifier.instance.notify_report_generated(@report)

  # we bring all xml together
  report_xml = "<report>#{CGI.unescapeHTML(@report.to_xml)}#{udv}#{findings_xml}#{udo_xml}#{services_xml}#{hosts_xml}#{all_appendices_xml}#{plugins_xml}</report>"
  noko_report_xml = Nokogiri::XML(report_xml)
  #no use to go on with report generation if report XML is malformed
  if !noko_report_xml.errors.empty?
    noko_report_xml.errors.each do |error|
      error = CGI.escapeHTML(error.to_s)
    end
    return "
The following error(s) were found in report XML file :
#{noko_report_xml.errors.join('
')}
This is most often because of malformed metamarkup in findings."
  end

  xslt_elem = Xslt.first(report_type: @report.report_type)

  # Push the finding from XML to XSLT
  xslt = Nokogiri::XSLT(File.read(xslt_elem.xslt_location))

  docx_xml = xslt.transform(Nokogiri::XML(report_xml))

  # We use a temporary file with a random name
  rand_file = "./tmp/#{rand(36**12).to_s(36)}.docx"

  # Create a temporary copy of the word doc
  FileUtils.copy_file(xslt_elem.docx_location, rand_file)

  list_components = {}
  xslt_elem.components.each do |component|
    xslt = Nokogiri::XSLT(File.read(component.xslt_location))
    list_components[component.name] = xslt.transform(Nokogiri::XML(report_xml))
  end
  ### IMAGE INSERT CODE
  if docx_xml.to_s =~ /\[!!/
    # first we read in the current [Content_Types.xml]
    content_types = read_rels(rand_file, '[Content_Types].xml')

    # add the png and jpg handling to end of content types document
    if content_types !~ /image\/jpg/
      content_types = content_types.sub('</Types>', '<Default Extension="jpg" ContentType="image/jpg"/></Types>')
    end
    if content_types !~ /image\/png/
      content_types = content_types.sub('</Types>', '<Default Extension="png" ContentType="image/png"/></Types>')
    end
    if content_types !~ /image\/jpeg/
      content_types = content_types.sub('</Types>', '<Default Extension="jpeg" ContentType="image/jpeg"/></Types>')
    end

    docx_modify(rand_file, content_types, '[Content_Types].xml')

    # replace all [!! image !!] in the document
    imgs = docx_xml.to_s.split('[!!')
    docx = imgs.first
    imgs.delete_at(0)

    imgs.each do |image_i|
      name = image_i.split('!!]').first.delete(' ')
      end_xml = image_i.split('!!]').last

      # search for the image in the attachments
      image = Attachments.first(description: name, report_id: id)

      # tries to prevent breakage in the case image dne
      if image
        # inserts the image
        docx = image_insert(docx, rand_file, image, end_xml)
      else
        docx << end_xml
      end
    end
  else
    # no images in finding
    docx = docx_xml.to_s
  end
  #### END IMAGE INSERT CODE

  # Get hyperlinks and References
  hyperlinks = updateHyperlinks(docx)
  # Update _rels directrory
  rels_file =  read_rels(rand_file, 'word/_rels/document.xml.rels')
  # Noko syntax rels
  noko_rels =  Nokogiri::XML(rels_file)
  urls = hyperlinks['urls']
  id = hyperlinks['id']
  for i in 0..id.length - 1
    url =  urls[i]
    cid =  id[i]
    noko_rels.root.first_element_child.after("<Relationship Id=\"#{cid}\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink\" Target=\"#{url.delete(' ')}\" TargetMode=\"External\"/>")
  end

  content_to_write = noko_rels.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML).strip
  # Edit Relationships file
  write_rels(rand_file, 'word/_rels/document.xml.rels', content_to_write)
  # Update hyperlinks
  docx = hyperlinks['xmlText']

  docx_modify(rand_file, docx, 'word/document.xml')

  list_components.each do |name, xml|
    docx_modify(rand_file, xml.to_s, name)
  end

  serpico_log("Report generation attempted, Report Name: #{@report.report_name} #{rand_file} #{xslt_elem.xslt_location}")
  send_file rand_file, type: 'docx', filename: "#{@report.report_name}.docx"
end

# Export a report
get '/report/:id/export' do
  json = {}

  id = params[:id]
  report = get_report(id)

  # bail without a report
  redirect to('/') unless report

  # add the report
  json['report'] = report

  # add the findings
  findings = Findings.all(report_id: id)
  json['findings'] = findings

  # add the exports
  attachments = Attachments.all(report_id: id)
  json['Attachments'] = attachments

  local_filename = "./tmp/#{rand(36**12).to_s(36)}.json"
  File.open(local_filename, 'w') { |f| f.write(JSON.pretty_generate(json)) }

  send_file local_filename, type: 'json', filename: 'exported_report.json'
end

# Import a report
get '/report/import' do
  haml :import_report
end

# Import a report
post '/report/import' do
  redirect to('/report/import') unless params[:file]

  # reject if the file is above a certain limit
  if params[:file][:tempfile].size > 100_000_000
    return 'File too large. 100MB limit'
  end

  json_file = params[:file][:tempfile].read
  line = JSON.parse(json_file)

  line['report']['id'] = nil

  f = Reports.create(line['report'])
  f.save

  # now add the findings
  line['findings'].each do |finding|
    finding['id'] = nil
    finding['master_id'] = nil
    finding['report_id'] = f.id
    finding['finding_modified'] = nil

    finding['dread_total'] = 0 if finding['dread_total'].nil?
    finding['cvss_total'] = 0 if finding['cvss_total'].nil?
    finding['risk'] = 1 if finding['risk'].nil?

    g = Findings.create(finding)
    g.save
  end

  if line['Attachments']
    # now add the attachments
    line['Attachments'].each do |attach|
      serpico_log("Importing attachments to #{f.id}")
      attach['id'] = nil

      attach['filename'] = 'Unknown' if attach['filename'].nil?
      if attach['filename_location'] =~ /./
        a = attach['filename_location'].split('.').last
        loc = './attachments/' + a.gsub('/attachments/', '')
        attach['filename_location'] = loc
      else
        loc = './attachments/' + attach['filename_location']
      end
      attach['filename_location'] = loc

      attach['report_id'] = f.id
      attach['description'] = 'No description' if attach['description'].nil?
      g = Attachments.create(attach)
      g.save
    end
  end

  # we should redirect to the newly imported report
  redirect to("/report/#{f.id}/edit")
end
get '/report/:id/text_status' do
  id = params[:id]
  @report = get_report(id)

  # bail without a report
  redirect to('/') unless @report

  # add the findings
  @findings = Findings.all(report_id: id)

  haml :text_status
end

# generate an asciidoc version of current findings
get '/report/:id/asciidoc_status' do
  id = params[:id]
  report = get_report(id)

  # bail without a report
  redirect to('/') unless report

  # add the findings
  findings = Findings.all(report_id: id)

  ascii_doc_ = ''
  findings.each do |finding|
    ascii_doc_ << gen_asciidoc(finding, report.scoring)
  end

  local_filename = "./tmp/#{rand(36**12).to_s(36)}.asd"
  File.open(local_filename, 'w') { |f| f.write(ascii_doc_) }

  send_file local_filename, type: 'txt', filename: "report_#{id}_findings.asd"
end

# generate a csv with the current report findings
get '/report/:id/csv_export' do
  id = params[:id]
  @report = get_report(id)

  # bail without a report
  redirect to('/') unless @report

  # add the findings
  @findings = Findings.all(report_id: id)
  csv_doc_ = "Finding Title|Risk Rating|Remediation Effort|Type|Overview|Remediation\n"
  @findings.each do |finding|
    csv_doc_ << "#{finding.title}|#{finding.risk}|#{finding.effort}|#{finding.type}|#{finding.overview}|#{finding.remediation}\n"
  end
  # change some text around so the findings actually make sense and don't have a ton of garbage in them
  csv_doc_ = csv_doc_.gsub(/<paragraph>/, '')
  csv_doc_ = csv_doc_.gsub(/<\/paragraph>/, '')
  csv_doc_ = csv_doc_.gsub(/\|0\|/, '|Informational|')
  csv_doc_ = csv_doc_.gsub(/\|1\|/, '|Low|')
  csv_doc_ = csv_doc_.gsub(/\|2\|/, '|Moderate|')
  csv_doc_ = csv_doc_.gsub(/\|3\|/, '|High|')
  csv_doc_ = csv_doc_.gsub(/\|4\|/, '|Critical|')
  local_filename = "./tmp/#{rand(36**12).to_s(36)}.csv"
  File.open(local_filename, 'w') { |f| f.write(csv_doc_) }
  send_file local_filename, type: 'txt', filename: "report_#{id}_findings.csv"
end

# generate a presentation of current report
get '/report/:id/presentation' do
  # check the user has installed reveal
  unless File.directory?(Dir.pwd + '/public/reveal.js')
    return "reveal.js not found in /public/ directory. To install:

 1. Goto [INSTALL_DIR]/public/
2.run 'git clone https://github.com/hakimel/reveal.js.git'
3. Restart Serpico"
  end

  id = params[:id]

  @report = get_report(id)

  # bail without a report
  redirect to('/') unless @report

  @findings, @dread, @cvss, @cvssv3, @risk, @riskmatrix,@nist800  = get_scoring_findings(@report)

  # add images into presentations
  @images = []
  @findings.each do |find|
    next unless find.presentation_points
    find.presentation_points.to_s.split('<paragraph>').each do |pp|
      a = {}
      next unless pp =~ /\[\!\!/
      img = pp.split('[!!')[1].split('!!]').first
      a['name'] = img
      if Attachments.first(description: img)
        img_p = Attachments.first(description: img)
      else
        return "attachment #{img} from vulnerability  #{find.title} doesn't exist. Did you mistype something?"
      end
      a['link'] = "/report/#{id}/attachments/" + img_p.id.to_s
      @images.push(a)
    end
  end

  haml :presentation, layout: false
end

# export presentation of current report in html format, inside a zip
get '/report/:id/presentation_export' do
  # check the user has installed reveal
  unless File.directory?(Dir.pwd + '/public/reveal.js')
    return "reveal.js not found in /public/ directory. To install:

 1. Goto [INSTALL_DIR]/public/
2.run 'git clone https://github.com/hakimel/reveal.js.git'
3. Restart Serpico"
    sleep(30)
    redirect to('/')
  end

  id = params[:id]

  @report = get_report(id)

  # bail without a report
  redirect to('/') unless @report

  @findings, @dread, @cvss, @cvssv3, @risk, @riskmatrix = get_scoring_findings(@report)

  # add images into presentations
  @images = []
  @findings.each do |find|
    next unless find.presentation_points
    find.presentation_points.to_s.split('<paragraph>').each do |pp|
      a = {}
      next unless pp =~ /\[\!\!/
      img = pp.split('[!!')[1].split('!!]').first
      a['name'] = img
      if Attachments.first(description: img)
        img_p = Attachments.first(description: img)
      else
        return "attachment #{img} from vulnerability  #{find.title} doesn't exist. Did you mistype something?"
      end
      a['link'] = "/report/#{id}/attachments/#{img_p.id}"
      @images.push(a)
    end
  end

  # create html file from haml template
  template = File.read(Dir.pwd + '/views/presentation.haml')
  haml_engine = Haml::Engine.new(template)
  output = haml_engine.render(Object.new, :@report => @report, :@findings => @findings, :@dread => @dread, :@cvss => @cvss, :@cvss3 => @cvss3, :@riskmatrix => @riskmatrix, :@images => @images)
  rand_file = Dir.pwd + "/tmp/#{rand(36**12).to_s(36)}.html"
  newHTML = Nokogiri::HTML(output)

  # Each link inside the HTML file is considered as a dependency that will need to be fixed to a relative local path
  dependencies = []

  # fix href and src based links in the html to relative local URL. This should cover most of the use cases.
  newHTML.css('[href]').each do |el|
    if el.attribute('href').to_s[1, 6] != 'report' && !(dependencies.include? el.attribute('href').to_s[1..-1])
      dependencies.push(el.attribute('href').to_s[1..-1])
    end
    el.set_attribute('href', '.' + el.attribute('href'))
  end

  newHTML.css('[src]').each do |el|
    if el.attribute('src').to_s[1, 6] != 'report' && !(dependencies.include? el.attribute('src').to_s[1..-1])
      dependencies.push(el.attribute('src').to_s[1..-1])
    end
    el.set_attribute('src', '.' + el.attribute('src'))
  end

  # *slightly ugly* way to fix links in the HTML that aren't in a href or src (for exemple in javascript)
  htmlDoc = newHTML.to_html
  # the regex match stuff like '/img/reveal.js/foo/lib.js', "/css/reveal.js/theme/special.css"
  link = htmlDoc[/(\'|\")(\/(img|js|css|reveal\.js|fonts)\/(\S*\/)*\S*\.\S*)(\'|\")/, 2]
  until link.nil?
    dependencies.push(link[1..-1]) unless dependencies.include? link[1..-1]
    htmlDoc[/(\'|\")(\/(img|js|css|reveal\.js|fonts)\/(\S*\/)*\S*\.\S*)(\'|\")/, 2] = ".#{link}"
    link = htmlDoc[/(\'|\")(\/(img|js|css|reveal\.js|fonts)\/(\S*\/)*\S*\.\S*)(\'|\")/, 2]
  end

  # save html with links fixed to a relative local path
  File.open(rand_file, 'w') do |f|
    f.write htmlDoc
  end

  @findings,@dread,@cvss,@cvssv3,@risk,@riskmatrix,@nist800 = get_scoring_findings(@report)
  rand_zip = Dir.pwd + "/tmp/#{rand(36**12).to_s(36)}.zip"

  # put the presentation and its dependencies (links, images, libraries...) in a zip file
  Zip.setup do |c|
    c.on_exists_proc = true
    c.continue_on_exists_proc = true
  end
  Zip::File.open(rand_zip, Zip::File::CREATE) do |zipfile|
    zipfile.add('presentation.html', rand_file)

    # put the public directory in the zip file.
    list_public_file = Dir.glob(Dir.pwd + '/public/**/*')
    list_public_file.each do |file|
      # don't add directory or .git files in the zip
      if file['.git'].nil? && File.file?(file)
        # if file is .js or .css, check if it has dependencies that needs to be fixed to relative local path
        if !file[/\.(js|css)$/].nil?
          file_content = File.read(file)
          until link.nil?
            file_content[/(\'|\")(\/(img|js|css|reveal\.js|fonts)\/(\S*\/)*\S*\.\S*)(\'|\")/, 2] = ".#{link}"
            link = file_content[/(\'|\")(\/(img|js|css|reveal\.js|fonts)\/(\S*\/)*\S*\.\S*)(\'|\")/, 2]
          end
          rand_temp_file = Dir.pwd + "/tmp/#{rand(36**12).to_s(36)}.tmp"
          File.open(rand_temp_file, 'w') do |f|
            f.write file_content
          end
          # remove Serpico/public from the file path and put it in the zip
          zipfile.add(file[(Dir.pwd + '/public/').length..-1], rand_temp_file)
        else
          # remove Serpico/public from the file path and put it in the zip
          zipfile.add(file[(Dir.pwd + '/public/').length..-1], file)
        end
      end
    end
    # put attachements in the zip
    @images.each do |images|
      img_p = Attachments.first(description: images['name'])
      zipfile.add("report/#{id}/attachments/#{img_p.id}", img_p.filename_location)
    end
  end

  send_file rand_zip, type: 'zip', filename: "#{@report.report_name}.zip"
end

# set msf rpc settings for report
get '/report/:id/msfsettings' do
  id = params[:id]
  @report = get_report(id)

  # bail without a report
  redirect to('/') unless @report

  @vulnmap = config_options['vulnmap']
  @msfsettings = RemoteEndpoints.first(report_id: id)

  haml :msfsettings
end

# set msf rpc settings for report
post '/report/:id/msfsettings' do
  id = params[:id]
  @report = get_report(id)

  # bail without a report
  redirect to('/') unless @report

  return 'Metasploit integration not enabled' unless config_options['vulnmap']

  msfsettings = RemoteEndpoints.first(report_id: id)

  if msfsettings
    msfsettings.update(ip: params[:ip], port: params[:port], workspace: params[:workspace], user: params[:user], pass: params[:pass])
  else
    msfsettings = RemoteEndpoints.new
    msfsettings['report_id'] = @report.id
    msfsettings['ip'] = params[:ip]
    msfsettings['port'] = params[:port]
    msfsettings['type'] = 'msfrpc'
    msfsettings['workspace'] = params[:workspace]
    msfsettings['user'] = params[:user]
    msfsettings['pass'] = params[:pass]
    msfsettings.save
  end

  redirect to("/report/#{@report.id}/findings")
end

# display hosts from msf db
get '/report/:id/hosts' do
  id = params[:id]
  @report = get_report(id)
  @vulnmap = config_options['vulnmap']

  # bail without a report
  redirect to('/') unless @report

  msfsettings = RemoteEndpoints.first(report_id: id)
  unless msfsettings
    return "You need to setup a metasploit RPC connection to use this feature. Do so here"
  end

  # setup msfrpc handler
  rpc = msfrpc(@report.id)
  if rpc == false
    return 'ERROR: Connection to metasploit failed. Make sure you have msfprcd running and the settings in Serpico are correct.'
  end

  # get hosts from msf db
  res = rpc.call('console.create')
  rpc.call('db.set_workspace', msfsettings.workspace)
  res = rpc.call('db.hosts', limit: 10_000)
  @hosts = res['hosts']

  haml :dbhosts
end

# display services from msf db
get '/report/:id/services' do
  id = params[:id]
  @report = get_report(id)
  @vulnmap = config_options['vulnmap']

  # bail without a report
  redirect to('/') unless @report

  msfsettings = RemoteEndpoints.first(report_id: id)
  unless msfsettings
    return "You need to setup a metasploit RPC connection to use this feature. Do so here"
  end

  # setup msfrpc handler
  rpc = msfrpc(@report.id)
  if rpc == false
    return 'ERROR: Connection to metasploit failed. Make sure you have msfprcd running and the settings in Serpico are correct.'
  end

  # get hosts from msf db
  res = rpc.call('console.create')
  rpc.call('db.set_workspace', msfsettings.workspace)
  # onlyup undocumented but it does exist
  res = rpc.call('db.services', limit: 10_000, only_up: true)
  @services = res['services']

  haml :dbservices
end

# display vulns from msf db
get '/report/:id/vulns' do
  id = params[:id]
  @report = get_report(id)
  @vulnmap = config_options['vulnmap']

  # bail without a report
  redirect to('/') unless @report

  msfsettings = RemoteEndpoints.first(report_id: id)
  unless msfsettings
    return "You need to setup a metasploit RPC connection to use this feature. Do so here"
  end

  # setup msfrpc handler
  rpc = msfrpc(@report.id)
  if rpc == false
    return 'connection to MSF RPC deamon failed. Make sure you have msfprcd running and the settings in Serpico are correct.'
  end

  # get vulns from msf db
  res = rpc.call('console.create')
  rpc.call('db.set_workspace', msfsettings.workspace)
  res = rpc.call('db.vulns', limit: 10_000)
  @vulns = res['vulns']

  haml :dbvulns
end

# autoadd vulns from msf db
get '/report/:id/import/vulns' do
  id = params[:id]
  @report = get_report(id)

  # bail without a report
  redirect to('/') unless @report

  return 'No Such Report' if @report.nil?

  return 'Metasploit integration not enabled.' unless config_options['vulnmap']

  add_findings = []
  dup_findings = []
  autoadd_hosts = {}

  # load msf settings
  msfsettings = RemoteEndpoints.first(report_id: id)
  unless msfsettings
    return "You need to setup a metasploit RPC connection to use this feature. Do so here"
  end

  # setup msfrpc handler
  rpc = msfrpc(@report.id)
  if rpc == false
    return 'connection to MSF RPC deamon failed. Make sure you have msfprcd running and the settings in Serpico are correct.'
  end

  # determine findings to add from vuln data
  vulns = get_vulns_from_msf(rpc, msfsettings.workspace)

  # load all findings
  @findings = TemplateFindings.all(order: [:title.asc])

  # determine findings to add from vuln data
  # host/ip is key, value is array of vuln ids
  vulns.keys.each do |i|
    vulns[i].each do |v|
      # if serpico finding id maps to a ref from MSF vuln db, add to report
      @mappings = VulnMappings.all(msf_ref: v)
      # add affected hosts for each finding
      next unless @mappings
      @mappings.each do |m|
        if autoadd_hosts[m.templatefindings_id]
          # only one host/url per finding (regardless of ports and urls). this should change in the future
          unless autoadd_hosts[m.templatefindings_id].include?(i)
            autoadd_hosts[m.templatefindings_id] << i
          end
        else
          autoadd_hosts[m.templatefindings_id] = []
          autoadd_hosts[m.templatefindings_id] << i
        end
        add_findings << m.templatefindings_id
      end
    end
  end

  add_findings = add_findings.uniq

  # create new findings from an import
  # TODO: This will duplicate if the user already has a nessus id mapped
  if config_options['auto_import']
    p 'auto_import function not supported with MSF intergration'
  end

  if add_findings.empty?
    redirect to("/report/#{id}/findings")
  else
    @autoadd = true

    add_findings.each do |finding|
      # if the finding already exists in the report dont add
      currentfindings = Findings.all(report_id: id)
      currentfindings.each do |cf|
        next unless cf.master_id == finding.to_i
        dup_findings << finding.to_i unless dup_findings.include?(finding.to_i)
        add_findings.delete(finding.to_i)
      end
    end
    @autoadd_hosts = autoadd_hosts
    @dup_findings = dup_findings.uniq
    @autoadd_findings = add_findings
  end
  haml :findings_add
end

# get enabled plugins
get '/report/:id/report_plugins' do
  id = params[:id]
  @report = get_report(id)

  # bail without a report
  redirect to('/') unless @report

  @menu = []
  Dir[File.join(File.dirname(__FILE__), '../plugins/**/', '*.json')].each do |lib|
    pl = JSON.parse(File.open(lib).read)
    a = {}
    next unless pl['enabled'] && pl['report_view']
    # add the plugin to the menu
    a['name'] = pl['name']
    a['description'] = pl['description']
    a['link'] = pl['link']
    @menu.push(a)
  end
  haml :enabled_plugins
end


text:
<!DOCTYPE html>

    <title>Site Title</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link rel="stylesheet" href="<?php  echo base_url(); ?>public/css/bootstrap.min.css" />

    <link rel="stylesheet" href="<?php  echo base_url(); ?>public/css/style.css" />

    <link rel="stylesheet" href="<?php  echo base_url(); ?>public/css/screen.css" />

<?php $this->load->view('members/dashboard_top'); ?>

                        <img src="<?php echo base_url(); ?>public/images/thumb/<?php if(!empty($getStep1ProfileDetails->image)){ echo $getStep1ProfileDetails->image; }else echo "no.png"; ?>
						" alt="Profile Picture" />

<a href="<?php echo base_url('members/edit_profile'); ?>">Edit profile info

<a href="<?php echo base_url('members/edit_about_yourself'); ?>">About yourself

<a href="<?php echo base_url('members/upload_profile_image'); ?>">Upload Image

<a href="<?php echo base_url('members/change_password'); ?>">Change Password

   <?php  echo  $this->session->flashdata('message'); ?>

Edit Profile

Edit about yourself

							<form role="form" METHOD="post" action="<?php echo base_url('members/edit_about_yourself'); ?>">

										Music:

                                                                            <textarea placeholder="Your Fav. Music" name="music" class="join-form-control second-form-textarea"><?php echo !empty($getStep2ProfileDetails->music) ? trim($getStep2ProfileDetails->music) : "";  ?></textarea>

										Movies:

										<textarea placeholder="Your Fav. Movies" name="movies" class="join-form-control second-form-textarea"><?php echo !empty($getStep2ProfileDetails->movies) ? trim($getStep2ProfileDetails->movies) : "";  ?></textarea>

										TV:

										<textarea placeholder="Your Fav. TV Shows" name="tv" class="join-form-control second-form-textarea"><?php echo !empty($getStep2ProfileDetails->tv) ? trim($getStep2ProfileDetails->tv) : "";  ?></textarea>

										Books:

										<textarea placeholder="Your Fav. Books" name="books" class="join-form-control second-form-textarea"><?php echo !empty($getStep2ProfileDetails->books) ? trim($getStep2ProfileDetails->books) : "";  ?></textarea>

										Sports:

										<textarea placeholder="Your Fav. Sports" name="sports" class="join-form-control second-form-textarea"><?php echo !empty($getStep2ProfileDetails->sports) ? trim($getStep2ProfileDetails->sports) : "";  ?></textarea>

										Interests:

										<textarea placeholder="Your Interests" name="interests" class="join-form-control second-form-textarea"><?php echo !empty($getStep2ProfileDetails->interests) ? trim($getStep2ProfileDetails->interests) : "";  ?></textarea>

										Dreams:

										<textarea placeholder="Your Dreams" name="dreams" class="join-form-control second-form-textarea"><?php echo !empty($getStep2ProfileDetails->dreams) ? trim($getStep2ProfileDetails->dreams) : "";  ?></textarea>

										Best Feature:

										<textarea placeholder="Your Best Feature" name="best_feature" class="join-form-control second-form-textarea"><?php echo !empty($getStep2ProfileDetails->best_feature) ? trim($getStep2ProfileDetails->best_feature) : "";  ?></textarea>

										About Me:

										<textarea placeholder="About Me" name="about_me" class="join-form-control second-form-textarea"><?php echo !empty($getStep2ProfileDetails->about_me) ? trim($getStep2ProfileDetails->about_me) : "";  ?></textarea>

										<input type="submit" value="Save" class="add-detail-submit-button" />

											or
										<a class="add-detail-skip" href="<?php echo base_url('dashboard/index'); ?>"> Cancel

							</form>

					&copy 2015 Website Name. All rights reserved. Privacy Ploicy | Terms of Service

redpajama_set_name: RedPajamaGithub


text:
#include "conf_usb.h"
#include "usb_protocol.h"
#include "usb_protocol_vendor.h"
#include "udd.h"
#include "udc.h"
#include "udi_vendor.h"
#include <string.h>

// Configuration check
#ifndef UDI_VENDOR_ENABLE_EXT
# error UDI_VENDOR_ENABLE_EXT must be defined in conf_usb.h file.
#endif
#ifndef UDI_VENDOR_DISABLE_EXT
# error UDI_VENDOR_DISABLE_EXT must be defined in conf_usb.h file.
#endif

/**
 * \ingroup udi_vendor_group
 * \defgroup udi_vendor_group_udc Interface with USB Device Core (UDC)
 *
 * Structures and functions required by UDC.
 *
 * @{
 */
bool udi_vendor_enable(void);
void udi_vendor_disable(void);
bool udi_vendor_setup(void);
uint8_t udi_vendor_getsetting(void);

//! Global structure which contains standard UDI API for UDC
UDC_DESC_STORAGE udi_api_t udi_api_vendor = {
	.enable = udi_vendor_enable,
	.disable = udi_vendor_disable,
	.setup = udi_vendor_setup,
	.getsetting = udi_vendor_getsetting,
	.sof_notify = NULL,
};
//@}

/**
 * \ingroup udi_vendor_group
 * \defgroup udi_vendor_group_internal Implementation of UDI Vendor Class
 *
 * Class internal implementation
 * @{
 */

//! USB descriptor alternate setting used
static uint8_t udi_vendor_alternate_setting = 0;

/**
 * \name Internal routines
 */
//@{
bool udi_vendor_enable(void)
{
	udi_vendor_alternate_setting = udc_get_interface_desc()->bAlternateSetting;
	if (1 == udi_vendor_alternate_setting) {
		// Call application callback
		// to notify that interface is enabled
		if (!UDI_VENDOR_ENABLE_EXT()) {
			return false;
		}
	}
	return true;
}

void udi_vendor_disable(void)
{
	if (1 == udi_vendor_alternate_setting) {
		UDI_VENDOR_DISABLE_EXT();
	}
}

bool udi_vendor_setup(void)
{
	if (Udd_setup_is_in()) {
		if ((Udd_setup_type() == USB_REQ_TYPE_VENDOR))
		{
			return UDI_VENDOR_SETUP_IN_RECEIVED();
		}
	}
	if (Udd_setup_is_out()) {
		if ((Udd_setup_type() == USB_REQ_TYPE_VENDOR)
				&& (0 != udd_g_ctrlreq.req.wLength)) {
			return UDI_VENDOR_SETUP_OUT_RECEIVED();
		}
	}
	return false; // Not supported request
}

uint8_t udi_vendor_getsetting(void)
{
	return udi_vendor_alternate_setting;
}
//@}

#if UDI_VENDOR_EPS_SIZE_INT_FS
/**
 * \brief Start a transfer on interrupt IN
 *
 * When the transfer is finished or aborted (stall, reset, ...), the \a callback is called.
 * The \a callback returns the transfer status and eventually the number of byte transfered.
 *
 * \param buf           Buffer on Internal RAM to send or fill.
 *                      It must be align, then use COMPILER_WORD_ALIGNED.
 * \param buf_size      Buffer size to send or fill
 * \param callback      NULL or function to call at the end of transfer
 *
 * \return \c 1 if function was successfully done, otherwise \c 0.
 */
bool udi_vendor_interrupt_in_run(uint8_t * buf, iram_size_t buf_size,
		udd_callback_trans_t callback)
{
	return udd_ep_run(UDI_VENDOR_EP_INTERRUPT_IN,
			false,
			buf,
			buf_size,
			callback);
}

/**
 * \brief Start a transfer on interrupt OUT
 *
 * When the transfer is finished or aborted (stall, reset, ...), the \a callback is called.
 * The \a callback returns the transfer status and eventually the number of byte transfered.
 *
 * \param buf           Buffer on Internal RAM to send or fill.
 *                      It must be align, then use COMPILER_WORD_ALIGNED.
 * \param buf_size      Buffer size to send or fill
 * \param callback      NULL or function to call at the end of transfer
 *
 * \return \c 1 if function was successfully done, otherwise \c 0.
 */
bool udi_vendor_interrupt_out_run(uint8_t * buf, iram_size_t buf_size,
		udd_callback_trans_t callback)
{
	return udd_ep_run(UDI_VENDOR_EP_INTERRUPT_OUT,
			false,
			buf,
			buf_size,
			callback);
}
#endif

#if UDI_VENDOR_EPS_SIZE_BULK_FS
/**
 * \brief Start a transfer on bulk IN
 *
 * When the transfer is finished or aborted (stall, reset, ...), the \a callback is called.
 * The \a callback returns the transfer status and eventually the number of byte transfered.
 *
 * \param buf           Buffer on Internal RAM to send or fill.
 *                      It must be align, then use COMPILER_WORD_ALIGNED.
 * \param buf_size      Buffer size to send or fill
 * \param callback      NULL or function to call at the end of transfer
 *
 * \return \c 1 if function was successfully done, otherwise \c 0.
 */
bool udi_vendor_bulk_in_run(uint8_t * buf, iram_size_t buf_size,
		udd_callback_trans_t callback)
{
	return udd_ep_run(UDI_VENDOR_EP_BULK_IN,
			false,
			buf,
			buf_size,
			callback);
}

/**
 * \brief Start a transfer on bulk OUT
 *
 * When the transfer is finished or aborted (stall, reset, ...), the \a callback is called.
 * The \a callback returns the transfer status and eventually the number of byte transfered.
 *
 * \param buf           Buffer on Internal RAM to send or fill.
 *                      It must be align, then use COMPILER_WORD_ALIGNED.
 * \param buf_size      Buffer size to send or fill
 * \param callback      NULL or function to call at the end of transfer
 *
 * \return \c 1 if function was successfully done, otherwise \c 0.
 */
bool udi_vendor_bulk_out_run(uint8_t * buf, iram_size_t buf_size,
		udd_callback_trans_t callback)
{
	return udd_ep_run(UDI_VENDOR_EP_BULK_OUT,
			false,
			buf,
			buf_size,
			callback);
}
#endif

#if UDI_VENDOR_EPS_SIZE_ISO_FS
/**
 * \brief Start a transfer on interrupt IN
 *
 * When the transfer is finished or aborted (stall, reset, ...), the \a callback is called.
 * The \a callback returns the transfer status and eventually the number of byte transfered.
 *
 * \param buf           Buffer on Internal RAM to send or fill.
 *                      It must be align, then use COMPILER_WORD_ALIGNED.
 * \param buf_size      Buffer size to send or fill
 * \param callback      NULL or function to call at the end of transfer
 *
 * \return \c 1 if function was successfully done, otherwise \c 0.
 */
bool udi_vendor_iso_in_run(uint8_t * buf, iram_size_t buf_size,
		udd_callback_trans_t callback)
{
	return udd_ep_run(UDI_VENDOR_EP_ISO_IN,
			false,
			buf,
			buf_size,
			callback);
}

/**
 * \brief Start a transfer on interrupt OUT
 *
 * When the transfer is finished or aborted (stall, reset, ...), the \a callback is called.
 * The \a callback returns the transfer status and eventually the number of byte transfered.
 *
 * \param buf           Buffer on Internal RAM to send or fill.
 *                      It must be align, then use COMPILER_WORD_ALIGNED.
 * \param buf_size      Buffer size to send or fill
 * \param callback      NULL or function to call at the end of transfer
 *
 * \return \c 1 if function was successfully done, otherwise \c 0.
 */
bool udi_vendor_iso_out_run(uint8_t * buf, iram_size_t buf_size,
		udd_callback_trans_t callback)
{
	return udd_ep_run(UDI_VENDOR_EP_ISO_OUT,
			false,
			buf,
			buf_size,
			callback);
}
#endif

//@}


text:
class TagsController < ApplicationController
	def index
		authorize Tag
		@page = params[:page] ? params[:page] : 1
		@per_page = Kaminari.config.default_per_page
		@tags = Tag.owned_by(current_user).page @page
	end

	def new
		authorize Tag
		@tag = Tag.new
	end

	def create
		authorize Tag
		Tag.create(tag_params)
		redirect_to tags_path
	end

	def edit
		@tag = Tag.friendly.find(params[:id])
		authorize @tag
	end

	def update
		tag = Tag.friendly.find(params[:id])
		authorize tag
		Tag.update(tag, tag_params)
		redirect_to tags_path
	end

	def destroy
		Tag.destroy(params[:id])
		redirect_to tags_path
	end

	def show
		@tag = Tag.friendly.find(params[:id])
		authorize @tag
	end

	def search
		# lock it down for now
		authorize Tag
		# should move this functionality to run directly from the web server ahead of Rails (metal)
		# - needs to accept a second param, exclusion tags from the result (becasue they've been selected/applied already)
		@tags = Tag.where("name LIKE ?", "%#{params[:q]}%")
		respond_to do |format|
			# format.html {render :text => "search: #{params[:q]} => #{@tags.map(&:name)}", status: :ok}
			format.html {render :json => @tags, status: :ok}
			format.js {render :json => @tags, status: :ok}
		end
	end

	private
		def tag_params
			params.require(:tag).permit(:name)
		end
end


text:
'use strict';

jest.disableAutomock();

const path = require('path');

const rootDir = path.resolve(__dirname, 'test_root');
const testRegex = path.sep + '__testtests__' + path.sep;
const maxWorkers = 1;

let normalizeConfig;

describe('SearchSource', () => {
  const name = 'SearchSource';
  let Runtime;
  let hasteMap;
  let SearchSource;
  let searchSource;

  beforeEach(() => {
    Runtime = require('jest-runtime');
    SearchSource = require('../SearchSource');
    normalizeConfig = require('jest-config').normalize;
  });

  describe('isTestFilePath', () => {
    let config;

    beforeEach(() => {
      config = normalizeConfig({
        name,
        rootDir: '.',
        testPathDirs: [],
      });
      hasteMap = Runtime.buildHasteMap(config, {maxWorkers});
      searchSource = new SearchSource(hasteMap, config);
    });

    it('supports ../ paths and unix separators', () => {
      if (process.platform !== 'win32') {
        const path = '/path/to/__tests__/foo/bar/baz/../../../test.js';
        expect(searchSource.isTestFilePath(path)).toEqual(true);
      }
    });

    it('supports unix separators', () => {
      if (process.platform !== 'win32') {
        const path = '/path/to/__tests__/test.js';
        expect(searchSource.isTestFilePath(path)).toEqual(true);
      }
    });

    it('supports win32 separators', () => {
      if (process.platform === 'win32') {
        const path = '\\path\\to\\__tests__\\test.js';
        expect(searchSource.isTestFilePath(path)).toEqual(true);
      }
    });
  });

  describe('testPathsMatching', () => {
    it('finds tests matching a pattern', () => {
      const config = normalizeConfig({
        name,
        rootDir,
        moduleFileExtensions: ['js', 'jsx', 'txt'],
        testRegex: 'not-really-a-test',
      });
      const hasteMap = Runtime.buildHasteMap(config, {maxWorkers});
      const searchSource = new SearchSource(hasteMap, config);
      return searchSource.findMatchingTests().then(data => {
        const relPaths = data.paths.map(absPath => (
          path.relative(rootDir, absPath)
        ));
        expect(relPaths).toEqual([
          path.normalize('__testtests__/not-really-a-test.txt'),
        ]);
      });
    });

    it('finds tests matching a JS pattern', () => {
      const config = normalizeConfig({
        name,
        rootDir,
        moduleFileExtensions: ['js', 'jsx'],
        testRegex: 'test\.jsx?',
      });
      const hasteMap = Runtime.buildHasteMap(config, {maxWorkers});
      const searchSource = new SearchSource(hasteMap, config);
      return searchSource.findMatchingTests().then(data => {
        const relPaths = data.paths.map(absPath => (
          path.relative(rootDir, absPath)
        ));
        expect(relPaths.sort()).toEqual([
          path.normalize('__testtests__/test.js'),
          path.normalize('__testtests__/test.jsx'),
        ]);
      });
    });

    it('finds tests with default file extensions', () => {
      const config = normalizeConfig({
        name,
        rootDir,
        testRegex,
      });
      const hasteMap = Runtime.buildHasteMap(config, {maxWorkers});
      const searchSource = new SearchSource(hasteMap, config);
      return searchSource.findMatchingTests().then(data => {
        const relPaths = data.paths.map(absPath => (
          path.relative(rootDir, absPath)
        ));
        expect(relPaths).toEqual([
          path.normalize('__testtests__/test.js'),
        ]);
      });
    });

    it('finds tests with similar but custom file extensions', () => {
      const config = normalizeConfig({
        name,
        rootDir,
        testRegex,
        moduleFileExtensions: ['jsx'],
      });
      const hasteMap = Runtime.buildHasteMap(config, {maxWorkers});
      const searchSource = new SearchSource(hasteMap, config);
      return searchSource.findMatchingTests().then(data => {
        const relPaths = data.paths.map(absPath => (
          path.relative(rootDir, absPath)
        ));
        expect(relPaths).toEqual([
          path.normalize('__testtests__/test.jsx'),
        ]);
      });
    });

    it('finds tests with totally custom foobar file extensions', () => {
      const config = normalizeConfig({
        name,
        rootDir,
        testRegex,
        moduleFileExtensions: ['foobar'],
      });
      const hasteMap = Runtime.buildHasteMap(config, {maxWorkers});
      const searchSource = new SearchSource(hasteMap, config);
      return searchSource.findMatchingTests().then(data => {
        const relPaths = data.paths.map(absPath => (
          path.relative(rootDir, absPath)
        ));
        expect(relPaths).toEqual([
          path.normalize('__testtests__/test.foobar'),
        ]);
      });
    });

    it('finds tests with many kinds of file extensions', () => {
      const config = normalizeConfig({
        name,
        rootDir,
        testRegex,
        moduleFileExtensions: ['js', 'jsx'],
      });
      const hasteMap = Runtime.buildHasteMap(config, {maxWorkers});
      const searchSource = new SearchSource(hasteMap, config);
      return searchSource.findMatchingTests().then(data => {
        const relPaths = data.paths.map(absPath => (
          path.relative(rootDir, absPath)
        ));
        expect(relPaths.sort()).toEqual([
          path.normalize('__testtests__/test.js'),
          path.normalize('__testtests__/test.jsx'),
        ]);
      });
    });

    it('supports legacy APIs', () => {
      const config = normalizeConfig({
        name,
        rootDir,
        testDirectoryName: '__testtests__',
      });
      const hasteMap = Runtime.buildHasteMap(config, {maxWorkers});
      const searchSource = new SearchSource(hasteMap, config);
      return searchSource.findMatchingTests().then(data => {
        const relPaths = data.paths.map(absPath => (
          path.relative(rootDir, absPath)
        ));
        expect(relPaths).toEqual([
          path.normalize('__testtests__/test.js'),
        ]);
      });
    });

    it('supports legacy APIs', () => {
      const config = normalizeConfig({
        name,
        rootDir,
        testFileExtensions: ['js', 'jsx'],
      });
      const hasteMap = Runtime.buildHasteMap(config, {maxWorkers});
      const searchSource = new SearchSource(hasteMap, config);
      return searchSource.findMatchingTests().then(data => {
        const relPaths = data.paths.map(absPath => (
          path.relative(rootDir, absPath)
        ));
        expect(relPaths.sort()).toEqual([
          path.normalize('__testtests__/test.js'),
          path.normalize('__testtests__/test.jsx'),
          path.normalize('module.jsx'),
        ]);
      });
    });

    it('supports legacy APIs', () => {
      const config = normalizeConfig({
        name,
        rootDir,
        testDirectoryName: '__testtests__',
        testFileExtensions: ['js', 'jsx', 'foobar'],
      });
      const hasteMap = Runtime.buildHasteMap(config, {maxWorkers});
      const searchSource = new SearchSource(hasteMap, config);
      return searchSource.findMatchingTests().then(data => {
        const relPaths = data.paths.map(absPath => (
          path.relative(rootDir, absPath)
        ));
        expect(relPaths.sort()).toEqual([
          path.normalize('__testtests__/test.foobar'),
          path.normalize('__testtests__/test.js'),
          path.normalize('__testtests__/test.jsx'),
        ]);
      });
    });
  });

  describe('findRelatedTests', () => {
    const rootDir = path.join(
      __dirname,
      '..',
      '..',
      '..',
      'jest-runtime',
      'src',
      '__tests__',
      'test_root',
    );
    const rootPath = path.join(rootDir, 'root.js');

    beforeEach(() => {
      const config = normalizeConfig({
        name: 'SearchSource-findRelatedTests-tests',
        rootDir,
      });
      hasteMap = Runtime.buildHasteMap(config, {maxWorkers});
      searchSource = new SearchSource(hasteMap, config);
    });

    it('makes sure a file is related to itself', () => {
      return searchSource.findRelatedTests(new Set([rootPath]))
        .then(data => {
          expect(data.paths).toEqual([rootPath]);
        });
    });

    it('finds tests that depend directly on the path', () => {
      const filePath = path.join(rootDir, 'RegularModule.js');
      const parentDep = path.join(rootDir, 'ModuleWithSideEffects.js');
      return searchSource.findRelatedTests(new Set([filePath]))
        .then(data => {
          expect(data.paths.sort()).toEqual([
            parentDep,
            filePath,
            rootPath,
          ]);
        });
    });
  });
});


text:
'use strict';

// MODULES //

var factory = require( './factory.js' );

// MAIN //

/**
* Ranks a user's followers.
*
* @param {Object} opts - function options
* @param {string} [opts.token] - GitHub access token
* @param {string} [opts.username] - GitHub username
* @param {string} [opts.useragent] - user agent string
* @param {string} [opts.method="followers"] - rank method
* @param {Function} clbk - callback to invoke upon ranking a user's followers
* @returns {void}
*/
function rank( opts, clbk ) {
	factory( opts, clbk )();
}

// EXPORTS //

module.exports = rank;


text:
%
%update the GUI with new information. called once for each channel
%
%
%
function pContinuous_updateGUI( CSCChanNr, transferedGUIData, handlesPlugin, handlesParent )

%skip if disabled entirely or only enabled for first channel
if transferedGUIData.plotMode==2 ||  (transferedGUIData.plotMode==3 && CSCChanNr>1)
    return;
end
%transferedGUIData.plotMode

if ~isempty( transferedGUIData.xdata)
    %get existing value and only replace with what is new
    ydata = get( handlesPlugin.lineHandles.plotLine1_axesCSCall(CSCChanNr), 'ydata');
    ydata( transferedGUIData.xdata ) = transferedGUIData.filteredDataLFP+(CSCChanNr-1)*handlesParent.StimOMaticConstants.plotOffsetRawband;

    set( handlesPlugin.lineHandles.plotLine1_axesCSCall(CSCChanNr), 'ydata', ydata);

    ydata = get( handlesPlugin.lineHandles.plotLine1_axesSpAll(CSCChanNr), 'ydata');
    ydata( transferedGUIData.xdata ) = transferedGUIData.filteredDataSpikes+(CSCChanNr-1)*handlesParent.StimOMaticConstants.plotOffsetSpikeband;
    set( handlesPlugin.lineHandles.plotLine1_axesSpAll(CSCChanNr), 'ydata', ydata );
end

appdata = getappdata( handlesPlugin.figHandle);
handles = appdata.UsedByGUIData_m;

prevVal='';
if CSCChanNr>1
    prevVal=get( handles.textStatsSpikeband, 'String' );
end
set( handles.textStatsSpikeband, 'String', [prevVal ' '  num2str(transferedGUIData.spikesSd, '%.1f') ' (' num2str(CSCChanNr) ')']);

%set( handlesPlugin.textStatsLFPband, 'String', )


text:
//
// Copyright (c) Microsoft.  All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

namespace Microsoft.Azure.Commands.ApiManagement.ServiceManagement.Commands
{
    using System;
    using System.Linq;
    using System.Management.Automation;
    using Microsoft.Azure.Commands.ApiManagement.ServiceManagement.Models;

    [Cmdlet(VerbsCommon.New, "AzureRmApiManagementApi")]
    [OutputType(typeof(PsApiManagementApi))]
    public class NewAzureApiManagementApi : AzureApiManagementCmdletBase
    {
        [Parameter(
            ValueFromPipelineByPropertyName = true,
            Mandatory = true,
            HelpMessage = "Instance of PsApiManagementContext. This parameter is required.")]
        [ValidateNotNullOrEmpty]
        public PsApiManagementContext Context { get; set; }

        [Parameter(
            ValueFromPipelineByPropertyName = true,
            Mandatory = false,
            HelpMessage = "Identifier for new API. This parameter is optional. If not specified the identifier will be generated.")]
        public String ApiId { get; set; }

        [Parameter(
            ValueFromPipelineByPropertyName = true,
            Mandatory = true,
            HelpMessage = "Web API name. Public name of the API as it would appear on the developer and admin portals. This parameter is required.")]
        [ValidateNotNullOrEmpty]
        public String Name { get; set; }

        [Parameter(
            ValueFromPipelineByPropertyName = true,
            Mandatory = false,
            HelpMessage = "Web API description. This parameter is optional.")]
        public String Description { get; set; }

        [Parameter(
            ValueFromPipelineByPropertyName = true,
            Mandatory = true,
            HelpMessage = "A URL of the web service exposing the API. This URL will be used by Azure API Management only, and will not be made public. Must be 1 to 2000 characters long. This parameter is required.")]
        [ValidateNotNullOrEmpty]
        public String ServiceUrl { get; set; }

        [Parameter(
            ValueFromPipelineByPropertyName = true,
            Mandatory = false,
            HelpMessage = "Web API Path. Last part of the API's public URL. This URL will be used by API consumers for sending requests to the web service. Must be 1 to 400 characters long. This parameter is optional. Default value is $null.")]
        public String Path { get; set; }

        [Parameter(
            ValueFromPipelineByPropertyName = true,
            Mandatory = true,
            HelpMessage = "Web API protocols (http, https). Protocols over which API is made available. This parameter is required. Default value is $null.")]
        [ValidateNotNullOrEmpty]
        public PsApiManagementSchema[] Protocols { get; set; }

        [Parameter(
            ValueFromPipelineByPropertyName = true,
            Mandatory = false,
            HelpMessage = "OAuth authorization server identifier. This parameter is optional. Default value is $null. Must be specified if AuthorizationScope specified.")]
        public String AuthorizationServerId { get; set; }

        [Parameter(
            ValueFromPipelineByPropertyName = true,
            Mandatory = false,
            HelpMessage = "OAuth operations scope. This parameter is optional. Default value is $null.")]
        public String AuthorizationScope { get; set; }

        [Parameter(
            ValueFromPipelineByPropertyName = true,
            Mandatory = false,
            HelpMessage = "Subscription key header name. This parameter is optional. Default value is $null.")]
        public String SubscriptionKeyHeaderName { get; set; }

        [Parameter(
            ValueFromPipelineByPropertyName = true,
            Mandatory = false,
            HelpMessage = "Subscription key query string parameter name. This parameter is optional. Default value is $null.")]
        public String SubscriptionKeyQueryParamName { get; set; }

        [Parameter(
            ValueFromPipelineByPropertyName = true,
            Mandatory = false,
            HelpMessage = "Array of products IDs to add the new API to. This parameter is optional.")]
        public String[] ProductIds { get; set; }

        public override void ExecuteApiManagementCmdlet()
        {
            string id = ApiId ?? Guid.NewGuid().ToString("N");

            var newApi = Client.ApiCreate(
                Context,
                id,
                Name,
                Description,
                ServiceUrl,
                Path,
                Protocols.Distinct().ToArray(),
                AuthorizationServerId,
                AuthorizationScope,
                SubscriptionKeyHeaderName,
                SubscriptionKeyQueryParamName);

            if (ProductIds != null && ProductIds.Any())
            {
                WriteProgress(new ProgressRecord(0, "New-AzureRmApiManagementApi", "New API created. Adding to products..."));

                foreach (var productId in ProductIds)
                {
                    try
                    {
                        Client.ApiAddToProduct(Context, productId, id);
                        WriteProgress(
                            new ProgressRecord(
                                0,
                                "New-AzureRmApiManagementApi",
                                string.Format("... Added to product {0}", productId))
                            );
                    }
                    catch (Exception ex)
                    {
                        WriteProgress(
                            new ProgressRecord(
                                0,
                                "New-AzureRmApiManagementApi",
                                string.Format("... Failed to add to product {0} due to: {1}", productId, ex))
                            );
                    }
                }
            }

            WriteObject(newApi);
        }
    }
}


text:
module.exports.client = require('./client');
module.exports.server = require('./server');


text:
package org.apache.hadoop.hive.ql.wm;

import java.util.concurrent.TimeUnit;

import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.conf.Validator;
import org.apache.hadoop.hive.ql.parse.ASTNode;
import org.apache.hadoop.hive.ql.parse.HiveParser;
import org.apache.hadoop.hive.ql.parse.ParseDriver;
import org.apache.hadoop.hive.ql.parse.ParseException;
import org.apache.hadoop.hive.ql.plan.PlanUtils;

/**
 * Factory to create expressions
 */
public class ExpressionFactory {

  public static Expression fromString(final String expression) {
    if (expression == null || expression.isEmpty()) {
      return null;
    }

    ParseDriver driver = new ParseDriver();
    ASTNode node = null;
    try {
      node = driver.parseTriggerExpression(expression);
    } catch (ParseException e) {
      throw new IllegalArgumentException("Invalid expression: " + expression, e);
    }
    if (node.getChildCount() == 2 && node.getChild(1).getType() == HiveParser.EOF) {
      node = (ASTNode) node.getChild(0);
    }
    if (node.getType() != HiveParser.TOK_TRIGGER_EXPRESSION) {
      throw new IllegalArgumentException(
          "Expected trigger expression, got: " + node.toStringTree());
    }

    if (node.getChildCount() != 3) {
      throw new IllegalArgumentException("Only single > condition supported: " + expression);
    }

    // Only ">" predicate is supported right now, this has to be extended to support
    // expression tree when multiple conditions are required. HIVE-17622
    if (node.getChild(1).getType() != HiveParser.GREATERTHAN) {
      throw new IllegalArgumentException("Invalid predicate in expression");
    }

    final String counterName = node.getChild(0).getText();
    final String counterValueStr = PlanUtils.stripQuotes(
        node.getChild(2).getText().toLowerCase());
    if (counterName.isEmpty()) {
      throw new IllegalArgumentException("Counter name cannot be empty!");
    }

    // look for matches in file system counters
    long counterValue;
    for (FileSystemCounterLimit.FSCounter fsCounter : FileSystemCounterLimit.FSCounter.values()) {
      if (counterName.toUpperCase().endsWith(fsCounter.name())) {
        try {
          counterValue = getCounterValue(counterValueStr, new Validator.SizeValidator());
          if (counterValue < 0) {
            throw new IllegalArgumentException("Illegal value for counter limit. Expected a positive long value.");
          }
        } catch (NumberFormatException e) {
          throw new IllegalArgumentException("Invalid counter value: " + counterValueStr);
        }
        // this is file system counter, valid and create counter
        FileSystemCounterLimit fsCounterLimit = FileSystemCounterLimit.fromName(counterName, counterValue);
        return createExpression(fsCounterLimit);
      }
    }

    // look for matches in time based counters
    for (TimeCounterLimit.TimeCounter timeCounter : TimeCounterLimit.TimeCounter.values()) {
      if (counterName.equalsIgnoreCase(timeCounter.name())) {
        try {
          counterValue = getCounterValue(counterValueStr, new Validator.TimeValidator(TimeUnit.MILLISECONDS));
          if (counterValue < 0) {
            throw new IllegalArgumentException("Illegal value for counter limit. Expected a positive long value.");
          }
        } catch (NumberFormatException e) {
          throw new IllegalArgumentException("Invalid counter value: " + counterValueStr);
        }
        TimeCounterLimit timeCounterLimit = new TimeCounterLimit(
          TimeCounterLimit.TimeCounter.valueOf(counterName.toUpperCase()), counterValue);
        return createExpression(timeCounterLimit);
      }
    }

    // look for matches in vertex specific counters
    for (VertexCounterLimit.VertexCounter vertexCounter : VertexCounterLimit.VertexCounter.values()) {
      if (counterName.equalsIgnoreCase(vertexCounter.name())) {
        try {
          counterValue = getCounterValue(counterValueStr, null);
          if (counterValue < 0) {
            throw new IllegalArgumentException("Illegal value for counter limit. Expected a positive long value.");
          }
        } catch (NumberFormatException e) {
          throw new IllegalArgumentException("Invalid counter value: " + counterValueStr);
        }
        VertexCounterLimit vertexCounterLimit = new VertexCounterLimit(
          VertexCounterLimit.VertexCounter.valueOf(counterName.toUpperCase()), counterValue);
        return createExpression(vertexCounterLimit);
      }
    }

    // if nothing matches, try creating a custom counter
    try {
      counterValue = getCounterValue(counterValueStr, null);
      if (counterValue < 0) {
        throw new IllegalArgumentException("Illegal value for counter limit. Expected a positive long value.");
      }
    } catch (NumberFormatException e) {
      throw new IllegalArgumentException("Invalid counter value: " + counterValueStr);
    }
    CustomCounterLimit customCounterLimit = new CustomCounterLimit(counterName, counterValue);
    return createExpression(customCounterLimit);
  }

  private static long getCounterValue(final String counterValueStr, final Validator validator) throws
    NumberFormatException {
    long counter;
    try {
      counter = Long.parseLong(counterValueStr);
    } catch (NumberFormatException e) {
      if (validator != null) {
        if (validator instanceof Validator.SizeValidator) {
          return HiveConf.toSizeBytes(counterValueStr);
        } else if (validator instanceof Validator.TimeValidator) {
          return HiveConf.toTime(counterValueStr, TimeUnit.MILLISECONDS, TimeUnit.MILLISECONDS);
        }
      }
      throw e;
    }
    return counter;
  }

  static Expression createExpression(CounterLimit counterLimit) {
    return new TriggerExpression(counterLimit, Expression.Predicate.GREATER_THAN);
  }
}