#!/usr/bin/env python
"""
Gstreamer fuzzing project.
"""

INCR_MANGLE = False
TIMEOUT = 5
GST_LAUNCH = 'gst-launch-0.10'
NO_AUDIO = True
NO_VIDEO = True
VIDEO_EXTENSIONS = "avi,mkv,mov,mpg,mpeg,mp4"

from fusil.application import Application
from optparse import OptionGroup
from fusil.process.mangle import MangleProcess
from fusil.process.watch import WatchProcess
from fusil.process.stdout import WatchStdout
if INCR_MANGLE:
    from fusil.incr_mangle import IncrMangle
else:
    from fusil.auto_mangle import AutoMangle
from fusil.file_tools import filenameExtension

class Fuzzer(Application):
    NAME = "gstreamer"
    USAGE = "%prog [options] filename"
    NB_ARGUMENTS = 1

    def createFuzzerOptions(self, parser):
        options = OptionGroup(parser, "ImageMagick fuzzer")
        options.add_option("--playbin", help="Use playbin instead of decodebin",
            action="store_true")
        options.add_option("--gst-launch", help="gst-launch program path (default: %s)" % GST_LAUNCH,
            type="str", default=GST_LAUNCH)
        options.add_option("--timeout", help="Timeout in seconds (default: %s)" % TIMEOUT,
            type="float", default=TIMEOUT)
        options.add_option("--video", help="Enable video (default: use fakesink), always on with --playbin",
            action="store_true", default=False)
        options.add_option("--audio", help="Enable audio (default: use fakesink), always on with --playbin",
            action="store_true", default=False)
        options.add_option("--is-video", help="The file is a video (only used by decodebin) (default: guess using the filename extension)",
            action="store_true", default=False)
        options.add_option("--video-ext", help="Video filename extensions separated by commas (only used by decodebin) (default: %s)" % VIDEO_EXTENSIONS,
            type="str", default=VIDEO_EXTENSIONS)
        return options

    def setupProject(self):
        project = self.project

        # Profile parameters
        if self.options.audio:
            audio_sink = "alsasink"
        else:
            audio_sink = "fakesink"
        if self.options.video:
            video_sink = "xvimagesink"
        else:
            video_sink = "fakesink"

        # Create buggy input file
        orig_filename = self.arguments[0]
        if INCR_MANGLE:
            mangle = IncrMangle(project, orig_filename)
            mangle.max_size = 50*1024
            # OGG
            #mangle.operation_per_version = 10
            #mangle.max_version = 100
            # WAVE
            #mangle.operation_per_version = 100
            #mangle.max_version = 30
            # AVI
            mangle.operation_per_version = 500
            mangle.max_version = 50
        else:
            mangle = AutoMangle(project, orig_filename)
            mangle.hard_max_op = 500
            mangle.max_size = 10*1024*1024

        # -f option: Do not install a fault handler
        arguments = [self.options.gst_launch, '-f']

        if not self.options.playbin:
            arguments.extend((
                "filesrc", "location=<filename>",
                "!", "decodebin", "name=decoder",
                "decoder.",
                    "!", "queue",
                    "!", "audioconvert", "!", "audioresample", "!", audio_sink,
            ))
            if self.isVideo(orig_filename):
                arguments.extend((
                    "decoder.",
                        "!", "ffmpegcolorspace",
                        "!", video_sink,
                ))
        else:
            arguments.extend(('playbin', 'uri=file://<filename>'))

        process = MangleProcess(project, arguments, "<filename>",
            use_relative_mangle=False, timeout=self.options.timeout)
        if self.options.video:
            process.setupX11()

        WatchProcess(process, exitcode_score=0.20, timeout_score=0.20)

        stdout = WatchStdout(process)
        stdout.words['error'] = 0.10
        stdout.words['critical'] = 0.30
        del stdout.words['assertion']
        stdout.addRegex(r'Could not decode stream\.$', -1.0)
        stdout.addRegex(r'Could not (?:decode stream|determine type of stream|demultiplex stream)\.$', -1.0)
        stdout.addRegex(r'The stream is of a different type than handled by this element\.$', -1.0)
        stdout.addRegex(r'You might need to install the necessary plugins', 1.0)
        stdout.score_weight = 0.40

    def isVideo(self, filename):
        if self.options.is_video:
            return True
        file_ext = filenameExtension(filename)
        if file_ext:
            file_ext = file_ext.lower()
        video_extensions = self.options.video_ext.lower()
        video_extensions = set(('.%s' % ext) for ext in video_extensions.split(','))
        return (file_ext in video_extensions)

if __name__ == "__main__":
    Fuzzer().main()

